@weclapp/sdk 2.0.0-dev.4 → 2.0.0-dev.41
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 +415 -19
- package/dist/cli.js +565 -467
- package/package.json +42 -34
- package/tsconfig.sdk.json +14 -0
- package/tsconfig.lib.json +0 -17
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { fileURLToPath } from 'url';
|
|
2
2
|
import { resolve, dirname } from 'path';
|
|
3
|
-
import { rmdir, stat, readFile, writeFile, rm, cp, mkdir } from 'fs/promises';
|
|
4
3
|
import { rollup } from 'rollup';
|
|
5
4
|
import terser from '@rollup/plugin-terser';
|
|
6
|
-
import ts from 'rollup
|
|
5
|
+
import ts from '@rollup/plugin-typescript';
|
|
7
6
|
import indentString from 'indent-string';
|
|
8
|
-
import { snakeCase,
|
|
7
|
+
import { snakeCase, camelCase, pascalCase } from 'change-case';
|
|
9
8
|
import chalk from 'chalk';
|
|
10
9
|
import { OpenAPIV3 } from 'openapi-types';
|
|
11
10
|
import { createHash } from 'crypto';
|
|
11
|
+
import { stat, readFile, rm, cp, writeFile, mkdir } from 'fs/promises';
|
|
12
12
|
import { config } from 'dotenv';
|
|
13
13
|
import yargs from 'yargs';
|
|
14
14
|
import { hideBin } from 'yargs/helpers';
|
|
15
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
15
16
|
import prettyMs from 'pretty-ms';
|
|
16
17
|
|
|
18
|
+
const currentDirname = () => {
|
|
19
|
+
// Go one level up as the CLI is inside a folder
|
|
20
|
+
return fileURLToPath(new URL('..', import.meta.url));
|
|
21
|
+
};
|
|
22
|
+
|
|
17
23
|
var Target;
|
|
18
24
|
(function (Target) {
|
|
19
25
|
Target["BROWSER_PROMISES"] = "browser";
|
|
@@ -34,15 +40,10 @@ const resolveBinaryType = (target) => {
|
|
|
34
40
|
return isNodeTarget(target) ? 'Buffer' : 'Blob';
|
|
35
41
|
};
|
|
36
42
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return fileURLToPath(new URL('..', import.meta.url));
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const tsconfig = resolve(currentDirname(), './tsconfig.lib.json');
|
|
43
|
-
const resolveGlobals = (...globals) => Object.fromEntries(globals.map(v => [v, '*']));
|
|
43
|
+
const tsconfig = resolve(currentDirname(), './tsconfig.sdk.json');
|
|
44
|
+
const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, '*']));
|
|
44
45
|
const generateOutput = (config) => ({
|
|
45
|
-
sourcemap:
|
|
46
|
+
sourcemap: false,
|
|
46
47
|
banner: `/* weclapp sdk */`,
|
|
47
48
|
...config
|
|
48
49
|
});
|
|
@@ -61,35 +62,22 @@ const bundle = async (workingDirectory, target) => {
|
|
|
61
62
|
globals: resolveGlobals('node-fetch', 'url')
|
|
62
63
|
})
|
|
63
64
|
];
|
|
64
|
-
// Remove build dir
|
|
65
|
-
await rmdir(dist()).catch(() => void 0);
|
|
66
65
|
const bundles = {
|
|
67
66
|
[Target.BROWSER_PROMISES]: () => ({
|
|
68
|
-
input: src('
|
|
67
|
+
input: src('index.ts'),
|
|
68
|
+
plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
|
|
69
69
|
output: [
|
|
70
|
-
generateOutput({
|
|
71
|
-
file: dist('index.cjs'),
|
|
72
|
-
name: 'Weclapp',
|
|
73
|
-
format: 'umd'
|
|
74
|
-
}),
|
|
75
70
|
generateOutput({
|
|
76
71
|
file: dist('index.js'),
|
|
77
72
|
format: 'es'
|
|
78
73
|
})
|
|
79
|
-
]
|
|
80
|
-
plugins: [ts({ tsconfig }), terser()]
|
|
74
|
+
]
|
|
81
75
|
}),
|
|
82
76
|
[Target.BROWSER_RX]: () => ({
|
|
77
|
+
input: src('index.ts'),
|
|
78
|
+
plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
|
|
83
79
|
external: ['rxjs'],
|
|
84
|
-
input: src('browser.rx.ts'),
|
|
85
|
-
plugins: [ts({ tsconfig }), terser()],
|
|
86
80
|
output: [
|
|
87
|
-
generateOutput({
|
|
88
|
-
file: dist('index.cjs'),
|
|
89
|
-
name: 'Weclapp',
|
|
90
|
-
format: 'umd',
|
|
91
|
-
globals: resolveGlobals('rxjs')
|
|
92
|
-
}),
|
|
93
81
|
generateOutput({
|
|
94
82
|
file: dist('index.js'),
|
|
95
83
|
format: 'es',
|
|
@@ -98,16 +86,16 @@ const bundle = async (workingDirectory, target) => {
|
|
|
98
86
|
]
|
|
99
87
|
}),
|
|
100
88
|
[Target.NODE_PROMISES]: () => ({
|
|
101
|
-
input: src('
|
|
102
|
-
|
|
89
|
+
input: src('index.ts'),
|
|
90
|
+
plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
|
|
103
91
|
external: ['node-fetch', 'url'],
|
|
104
|
-
|
|
92
|
+
output: generateNodeOutput()
|
|
105
93
|
}),
|
|
106
94
|
[Target.NODE_RX]: () => ({
|
|
107
|
-
input: src('
|
|
108
|
-
|
|
95
|
+
input: src('index.ts'),
|
|
96
|
+
plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
|
|
109
97
|
external: ['node-fetch', 'url', 'rxjs'],
|
|
110
|
-
|
|
98
|
+
output: generateNodeOutput()
|
|
111
99
|
})
|
|
112
100
|
};
|
|
113
101
|
const config = bundles[target]();
|
|
@@ -139,14 +127,26 @@ const indent = (s, level = 1) => {
|
|
|
139
127
|
};
|
|
140
128
|
|
|
141
129
|
const generateStatements = (...statements) => statements
|
|
142
|
-
.map(v => v.trim())
|
|
143
|
-
.filter(v => v.length)
|
|
130
|
+
.map((v) => v.trim())
|
|
131
|
+
.filter((v) => v.length)
|
|
144
132
|
.join('\n\n');
|
|
145
133
|
const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
|
|
146
134
|
|
|
147
|
-
var
|
|
135
|
+
var globalConfig = "export type RequestPayloadMethod =\n | 'GET'\n | 'HEAD'\n | 'POST'\n | 'PUT'\n | 'DELETE'\n | 'CONNECT'\n | 'OPTIONS'\n | 'TRACE'\n | 'PATCH';\n\nexport interface RequestPayload {\n method?: RequestPayloadMethod;\n query?: Record<string, any>;\n body?: any;\n unwrap?: boolean;\n forceBlob?: boolean;\n}\n\nexport interface ServiceConfig {\n // Your API-Key, this is optional in the sense of if you omit this, and you're in a browser, the\n // cookie-authentication (include-credentials) will be used.\n key?: string;\n\n // Your domain, if omitted location.host will be used (browser env).\n host?: string;\n\n // If you want to use https, defaults to location.protocol (browser env).\n secure?: boolean;\n\n // If you want that some and count requests are bundled into multi requests.\n multiRequest?: boolean;\n\n // If you want that the ignoreMissingProperties parameter to be set to true for every post request.\n ignoreMissingProperties?: boolean;\n\n // Optional request/response interceptors.\n interceptors?: {\n // Takes the generated request, you can either return a new request,\n // a response (which will be taken as \"the\" response) or nothing.\n // The payload contains the raw input generated by the SDK.\n request?: (\n request: Request,\n payload: RequestPayload\n ) => Request | Response | void | Promise<Request | Response | void>;\n\n // Takes the response. This can either be the one from the server or an\n // artificially-crafted one by the request interceptor.\n response?: (response: Response) => Response | void | Promise<Response | void>;\n };\n\n // Whether POST should be used instead of GET for some() and count() operations\n usePost?: boolean;\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n}\n\ntype ServiceConfigWithoutMultiRequest = Omit<ServiceConfig, 'multiRequest'>;\n\nlet globalConfig: ServiceConfig | undefined;\nexport const getGlobalConfig = (): ServiceConfig | undefined => globalConfig;\nexport const setGlobalConfig = (cfg?: ServiceConfig) => (globalConfig = cfg);\n\nexport const getHost = (cfg: ServiceConfig) => {\n let host = cfg.host?.replace(/^https?:\\/\\//, '');\n if (!host && typeof location !== 'undefined') {\n host = location.host;\n }\n\n if (!host) {\n throw new Error('Please specify a host');\n }\n\n return host;\n};\n\nexport const getProtocol = (cfg: ServiceConfig) => {\n const protocol =\n cfg.secure !== undefined\n ? cfg.secure\n ? 'https:'\n : 'http:'\n : typeof location !== 'undefined'\n ? location.protocol\n : undefined;\n\n if (!protocol) {\n throw new Error('Please specify a protocol (secure)');\n }\n\n return protocol;\n};\n";
|
|
136
|
+
|
|
137
|
+
var multiRequest = "type RequestTask = {\n uri: string;\n resolve: (result: unknown) => void;\n reject: (error: unknown) => void;\n};\n\ntype BatchRequestTask = RequestTask & {\n settled: boolean;\n};\n\ntype MultiRequestResponse = {\n status: number;\n body: object;\n};\n\nlet microtaskQueued: boolean = false;\nconst tasksSet: Set<RequestTask> = new Set<RequestTask>();\n\nconst SQUARE_BRACKET_OPEN = '['.charCodeAt(0);\nconst COMMA = ','.charCodeAt(0);\nconst DECODER = new TextDecoder();\n\nconst readNextResponse = (bytes: Uint8Array) => {\n let headerStart: number | undefined = undefined;\n let commasSeen = 0;\n\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i];\n if (headerStart === undefined) {\n if (byte === SQUARE_BRACKET_OPEN || byte === COMMA) {\n headerStart = i + 1;\n }\n } else {\n if (byte === COMMA) {\n commasSeen++;\n }\n if (commasSeen === 2) {\n const headerArrayString = `[${DECODER.decode(bytes.subarray(headerStart, i))}]`;\n const [index, jsonLength] = JSON.parse(headerArrayString);\n if (!(typeof index === 'number') || !(typeof jsonLength === 'number')) {\n throw new Error(`unexpected header: ${headerArrayString}`);\n }\n\n const endIndex = i + 1 + jsonLength;\n if (endIndex > bytes.length) {\n // not all bytes available yet\n return undefined;\n }\n const jsonString = DECODER.decode(bytes.subarray(i + 1, endIndex));\n const data = JSON.parse(jsonString) as MultiRequestResponse;\n return {\n index,\n data,\n remainingBytes: bytes.subarray(endIndex)\n };\n }\n }\n }\n return undefined;\n};\n\nconst fetchMultiRequest = async (requests: string[]) => {\n const cfg = getGlobalConfig();\n\n if (!cfg) {\n throw new Error(`ServiceConfig missing.`);\n }\n\n const host = getHost(cfg);\n const protocol = getProtocol(cfg);\n\n return await fetch(`${protocol}//${host}/webapp/api/v2/batch/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(cfg.key && { AuthenticationToken: cfg.key })\n },\n body: JSON.stringify({ requests })\n });\n};\n\nconst rejectTasks = (tasks: BatchRequestTask[], error: unknown) => {\n for (const task of tasks) {\n if (!task.settled) {\n task.reject(error);\n }\n }\n};\n\nconst processStream = (\n { value: chunk, done }: ReadableStreamReadResult<Uint8Array>,\n remainingBytes: Uint8Array,\n reader: ReadableStreamDefaultReader<Uint8Array>,\n tasks: BatchRequestTask[]\n) => {\n if (done) {\n return;\n }\n if (chunk) {\n let bytes = new Uint8Array(remainingBytes.length + chunk.length);\n bytes.set(remainingBytes);\n bytes.set(chunk, remainingBytes.length);\n\n while (bytes.length) {\n const result = readNextResponse(bytes);\n if (!result) {\n break;\n }\n const task = tasks[result.index];\n if (result.data.status >= 100 && result.data.status < 400) {\n task.resolve({\n ...result.data.body\n });\n } else {\n task.reject({\n ...result.data.body\n });\n }\n task.settled = true;\n bytes = result.remainingBytes;\n }\n reader\n .read()\n .then((readResult) => processStream(readResult, bytes, reader, tasks))\n .catch((error) => rejectTasks(tasks, error));\n }\n};\n\nconst batch = async (tasks: BatchRequestTask[]) => {\n try {\n const requests = tasks.map(({ uri }) => uri);\n const resp = await fetchMultiRequest(requests);\n const reader = resp.body?.getReader();\n\n if (!reader) {\n throw new Error('Stream reader is undefined');\n }\n reader\n .read()\n .then((readResult) => processStream(readResult, new Uint8Array(0), reader, tasks))\n .catch((error) => rejectTasks(tasks, error));\n } catch (e) {\n rejectTasks(tasks, e);\n throw e;\n }\n};\n\nconst addTask = (task: RequestTask) => {\n tasksSet.add(task);\n\n if (!microtaskQueued) {\n queueMicrotask(() => {\n microtaskQueued = false;\n if (tasksSet.size > 0) {\n const batchTasks = Array.from(tasksSet).map((task) => ({ ...task, settled: false }));\n void batch(batchTasks);\n tasksSet.clear();\n }\n });\n microtaskQueued = true;\n }\n};\n\nconst addRequest = (uri: string) => new Promise((resolve, reject) => addTask({ uri, resolve, reject }));\n";
|
|
138
|
+
|
|
139
|
+
var queriesWithFilter = "export type EqualityOperator = 'EQ' | 'NE';\n\nexport type ComparisonOperator =\n | 'LT'\n | 'GT'\n | 'LE'\n | 'GE'\n | 'LIKE'\n | 'ILIKE'\n | 'NOT_LIKE'\n | 'NOT_ILIKE'\n | 'IEQ'\n | 'NOT_IEQ';\n\nexport type ArrayOperator = 'IN' | 'NOT_IN';\n\nexport type Operator = EqualityOperator | ComparisonOperator | ArrayOperator;\n\nexport type NonEmptyArray<T> = [T, ...T[]];\n\nexport type MapOperators<T> = { [K in EqualityOperator]?: T | null } & { [K in ComparisonOperator]?: T } & {\n [K in ArrayOperator]?: NonEmptyArray<T>;\n};\n\nexport type QueryFilter<T> = {\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\n ? U extends Record<any, any>\n ? QueryFilter<U>\n : MapOperators<U>\n : T[P] extends Record<any, any> | undefined\n ? QueryFilter<T[P]>\n : MapOperators<T[P]>;\n};\n\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n};\n\nexport type SomeQuery<E, F, I, P> = {\n serializeNulls?: boolean;\n include?: QuerySelect<I>;\n properties?: P;\n filter?: QueryFilter<F> & CustomAttributeFilter;\n select?: QuerySelect<E>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst equality: string[] = ['EQ', 'NE', 'IEQ', 'NOT_IEQ'];\n\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\n\nconst array: string[] = ['IN', 'NOT_IN'];\n\nconst filterMap: Record<Operator, string> = {\n EQ: 'eq',\n NE: 'ne',\n LT: 'lt',\n GT: 'gt',\n LE: 'le',\n GE: 'ge',\n LIKE: 'like',\n NOT_LIKE: 'notlike',\n ILIKE: 'ilike',\n NOT_ILIKE: 'notilike',\n IN: 'in',\n NOT_IN: 'notin',\n IEQ: 'ieq',\n NOT_IEQ: 'notieq'\n};\n\nconst flattenCustomAttributes = (obj: CustomAttributeFilter = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [id, filter] of Object.entries(obj)) {\n const key = `customAttribute${id}`;\n\n if (typeof filter === 'object') {\n for (const [prop, value] of Object.entries(filter)) {\n entries.push([`${key}.${prop}-eq`, String(value)]);\n }\n } else if (filter !== undefined) {\n entries.push([`${key}-eq`, String(filter)]);\n }\n }\n\n return entries;\n};\n\nconst flatten = (obj: QueryFilter<any> = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [prop, propValue] of Object.entries(obj)) {\n for (const [filter, value] of Object.entries(propValue as object)) {\n if (value === undefined) continue;\n\n if (simple.includes(filter) || array.includes(filter)) {\n if (value === null && equality.includes(filter)) {\n entries.push([`${prop}-${filter === 'EQ' ? 'null' : 'notnull'}`, '']);\n } else {\n entries.push([`${prop}-${filterMap[filter as Operator]}`, value]);\n }\n } else {\n entries.push(\n ...(flatten(propValue as QueryFilter<any>).map((v) => [`${prop}.${v[0]}`, v[1]]) as [string, string][])\n );\n break;\n }\n }\n }\n\n return entries;\n};\n\nconst flattenFilter = (obj: QueryFilter<any> = {}): Record<string, string> => {\n const filter: [string, any][] = [],\n customAttributes: [string, any][] = [];\n\n Object.entries(obj).forEach((value) => {\n (value[0].match(/^\\d+$/) ? customAttributes : filter).push(value);\n });\n\n return Object.fromEntries([\n ...flatten(Object.fromEntries(filter)),\n ...flattenCustomAttributes(Object.fromEntries(customAttributes) as CustomAttributeFilter)\n ]);\n};\n\nconst flattenOrFilter = (obj: QueryFilter<any>[] = []): Record<string, string> => {\n const entries: [string, any][] = [];\n\n for (let i = 0; i < obj.length; i++) {\n entries.push(...(flatten(obj[i]).map((v) => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]));\n }\n\n return Object.fromEntries(entries);\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & { params?: Record<any, any> },\n requestOptions?: RequestOptions\n) =>\n{\n const usePost = cfg?.usePost ?? globalConfig?.usePost\n const payload = {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\n }\n\n return wrapResponse(() =>\n raw(cfg, endpoint, {\n method: usePost ? 'POST' : 'GET',\n unwrap: true,\n ...(usePost ? { body: payload } : { query: payload })\n }, requestOptions)\n )\n };\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> },\n requestOptions?: RequestOptions\n) =>\n{\n const usePost = cfg?.usePost ?? globalConfig?.usePost\n const payload = {\n serializeNulls: query?.serializeNulls,\n additionalProperties: query?.properties?.join(','),\n properties: query?.select ? flattenSelect(query.select).join(',') : undefined,\n includeReferencedEntities: query?.include ? Object.keys(query.include).join(',') : undefined,\n ...flattenOrFilter(query?.or),\n ...flattenFilter(query?.filter),\n ...flattenSort(query?.sort),\n ...query?.params,\n ...query?.pagination\n }\n\n return wrapResponse(() =>\n raw(cfg, usePost ? `${endpoint}/query` : endpoint, {\n method: usePost ? 'POST' : 'GET',\n ...(usePost ? { body: payload } : { query: payload })\n }, requestOptions).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\n )\n };\n";
|
|
140
|
+
|
|
141
|
+
var queriesWithQueryLanguage = "export type ComparisonOperator =\n | 'EQ'\n | 'NE'\n | 'LT'\n | 'GT'\n | 'LE'\n | 'GE'\n | 'LIKE';\n\nexport type ArrayOperator = 'IN';\n\nexport type NullOperator = 'NULL';\n\nexport type Operator = ComparisonOperator | ArrayOperator | NullOperator;\n\nexport type ModifierFunction = 'lower';\n\nexport type NonEmptyArray<T> = [T, ...T[]];\n\nexport type MapOperators<T> =\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: NonEmptyArray<T> } & {\n [K in NullOperator]?: never } & {\n [K in ModifierFunction]?: boolean\n })\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: NonEmptyArray<T> } & {\n [K in NullOperator]?: boolean } & {\n [K in ModifierFunction]?: never\n });\n\nexport type SingleFilterExpr<T> = {\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\n ? U extends Record<any, any>\n ? SingleFilterExpr<U> | { NOT?: SingleFilterExpr<U> }\n : MapOperators<U>\n : T[P] extends Record<any, any> | undefined\n ? SingleFilterExpr<T[P]> | { NOT?: SingleFilterExpr<T[P]> }\n : MapOperators<T[P]>;\n};\n\nexport type QueryFilter<T> = SingleFilterExpr<T> & {\n OR?: QueryFilter<T>[];\n AND?: QueryFilter<T>[];\n NOT?: QueryFilter<T>;\n};\n\nexport type CountQuery<F> = {\n where?: QueryFilter<F>;\n};\n\nexport type SomeQuery<E, F, I, P> = {\n serializeNulls?: boolean;\n include?: QuerySelect<I>;\n properties?: P;\n where?: QueryFilter<F>;\n select?: QuerySelect<E>;\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst comparisonOperatorList: ComparisonOperator[] = [\n 'EQ',\n 'NE',\n 'LT',\n 'GT',\n 'LE',\n 'GE',\n 'LIKE'\n];\n\nconst comparisonOperatorMap: Record<Operator, string> = {\n EQ: '=',\n NE: '!=',\n LT: '<',\n GT: '>',\n LE: '<=',\n GE: '>=',\n LIKE: '~',\n IN: 'in',\n NULL: 'null'\n};\n\nconst modifierFunctionList: ModifierFunction[] = ['lower'];\n\nconst flattenWhere = (\n obj: QueryFilter<any> = {},\n nestedPaths: string[]\n): string[] => {\n const entries: string[] = [];\n for (const [prop, propValue] of Object.entries(obj)) {\n const setModifiers = findAllModifierFunctions(propValue ?? {}, modifierFunctionList).filter(\n (modifier) => modifier[1]\n );\n if (prop === 'OR') {\n const flattedOr: string[][] = [];\n for (let i = 0; i < (obj.OR?.length ?? 0); i++) {\n flattedOr.push(flattenWhere(obj.OR?.[i], nestedPaths));\n }\n entries.push(\n `(${flattedOr\n .map((x) => {\n const joined = x.join(' and ');\n\n if (x.length > 1) {\n return `(${joined})`;\n } else {\n return joined;\n }\n })\n .join(' or ')})`\n );\n } else if (prop === 'AND') {\n const flattedAnd: string[][] = [];\n for (let i = 0; i < (obj.AND?.length ?? 0); i++) {\n flattedAnd.push(flattenWhere(obj.AND?.[i], nestedPaths));\n }\n entries.push(\n `(${flattedAnd\n .map((x) => {\n const joined = x.join(' and ');\n\n if (x.length > 1) {\n return `(${joined})`;\n } else {\n return joined;\n }\n })\n .join(' and ')})`\n );\n } else if (prop === 'NOT') {\n const flattedNot = flattenWhere(obj.NOT, nestedPaths);\n entries.push(\n `not ${flattedNot.length > 1 ? '(' : ''}${flattedNot.join(' and ')}${flattedNot.length > 1 ? ')' : ''}`\n );\n } else if (propValue) {\n for (const [operator, value] of Object.entries(propValue)) {\n if (value === undefined) continue;\n if (comparisonOperatorList.includes(operator as ComparisonOperator)) {\n entries.push(\n `${setModifiers.reduce(\n (acc, [first]) => `${first}(${acc})`,\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\n )} ${comparisonOperatorMap[operator as Operator]} ${\n typeof value === 'string'\n ? setModifiers.reduce((acc, [first]) => `${first}(${acc})`, JSON.stringify(value))\n : value\n }`\n );\n } else if ((operator as Operator) === 'NULL') {\n entries.push(\n `${!value ? 'not ' : ''}${nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')} ${comparisonOperatorMap[operator as Operator]}`\n );\n } else if ((operator as Operator) === 'IN') {\n entries.push(\n `${setModifiers.reduce(\n (acc, [first]) => `${first}(${acc})`,\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\n )} ${comparisonOperatorMap[operator as Operator]} [${value.map((v: string | number) =>\n typeof v === 'string' ? setModifiers.reduce((acc, [first]) => `${first}(${acc})`, JSON.stringify(v)) : v\n )}]`\n );\n } else if (\n !modifierFunctionList.includes(operator as ModifierFunction)\n ) {\n entries.push(\n ...flattenWhere(propValue as QueryFilter<any>, [\n ...nestedPaths,\n prop\n ])\n );\n break;\n }\n }\n }\n }\n return entries;\n};\n\nconst assembleFilterParam = (\n obj: QueryFilter<any> = {}\n): Record<string, string> => {\n const flattedFilter = flattenWhere(obj, []);\n return flattedFilter.length ? { filter: flattedFilter.join(' and ') } : {};\n};\n\nconst findAllModifierFunctions = (\n obj: Record<string, any>,\n types: ModifierFunction[]\n) => {\n const result: Record<string, any> = {};\n for (const key in obj) {\n if (types.includes(key as ModifierFunction)) {\n result[key] = obj[key];\n }\n }\n return Object.entries(result);\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & { params?: Record<any, any> },\n requestOptions?: RequestOptions\n) =>\n {\n const usePost = cfg?.usePost ?? globalConfig?.usePost\n const payload = {\n ...assembleFilterParam(query?.where),\n ...query?.params\n }\n\n return wrapResponse(() =>\n raw(cfg, endpoint, {\n unwrap: true,\n method: usePost ? 'POST' : 'GET',\n ...(usePost ? { body: payload } : { query: payload })\n }, requestOptions)\n )\n };\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> },\n requestOptions?: RequestOptions\n) =>\n {\n const usePost = cfg?.usePost ?? globalConfig?.usePost\n const payload = {\n serializeNulls: query?.serializeNulls,\n additionalProperties: query?.properties?.join(','),\n properties: query?.select\n ? flattenSelect(query.select).join(',')\n : undefined,\n includeReferencedEntities: query?.include\n ? Object.keys(query.include).join(',')\n : undefined,\n ...assembleFilterParam(query?.where),\n ...flattenSort(query?.sort),\n ...query?.params,\n ...query?.pagination\n }\n\n return wrapResponse(() =>\n raw(cfg, usePost ? `${endpoint}/query` : endpoint, {\n method: usePost ? 'POST' : 'GET',\n ...(usePost ? { body: payload } : { query: payload })\n }, requestOptions).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\n )\n };\n";
|
|
142
|
+
|
|
143
|
+
var root = "export const raw = async (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n payload: RequestPayload = {},\n requestOptions?: RequestOptions\n): Promise<any> => {\n if (!cfg && !globalConfig) {\n throw new Error(`ServiceConfig missing.`);\n }\n\n const localCfg = {\n ...globalConfig,\n ...cfg,\n interceptors: { ...globalConfig?.interceptors, ...cfg?.interceptors }\n };\n\n const isBinaryData = payload.body instanceof resolveBinaryObject();\n const params = new URLSearchParams(Object.entries(payload.query ?? {}).filter((v) => v[1] !== undefined)\n .map(([key, value]) => [key, typeof value === 'string' ? value : JSON.stringify(value)])\n );\n\n const protocol = getProtocol(localCfg);\n\n const interceptRequest = localCfg.interceptors?.request ?? ((v) => v);\n const interceptResponse = localCfg.interceptors?.response ?? ((v) => v);\n\n const host = getHost(localCfg);\n\n let data;\n if (!cfg && localCfg.multiRequest) {\n let ep = endpoint;\n if (endpoint.startsWith('/')) {\n ep = endpoint.replace('/', '');\n }\n data = await addRequest(`${ep}?${params}`);\n } else {\n const request = new Request(`${protocol}//${host}/webapp/api/v${apiVersion}${endpoint}?${params}`, {\n ...(payload.body && {\n body: isBinaryData\n ? payload.body\n : JSON.stringify(payload.body, (_key, value) => (value === undefined ? null : value))\n }),\n ...(!localCfg.key && { credentials: 'same-origin' }),\n method: payload.method ?? 'get',\n headers: {\n Accept: 'application/json',\n ...(localCfg.key && { AuthenticationToken: localCfg.key }),\n ...(!isBinaryData && { 'Content-Type': 'application/json' })\n }\n });\n let res = (await interceptRequest(request, payload)) ?? request;\n if (!(res instanceof Response)) {\n res = requestOptions?.signal ? await fetch(res, { signal: requestOptions.signal } ) : await fetch(res);\n }\n res = (await interceptResponse(res)) ?? res;\n data =\n (!payload.forceBlob || !res.ok) && res.headers?.get('content-type')?.includes('application/json')\n ? await res.json()\n : await res.blob();\n\n // Check if response was successful\n if (!res.ok) {\n return Promise.reject(data);\n }\n }\n\n return payload.unwrap ? data.result : data;\n};\n\nconst _remove = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n { dryRun = false }: RemoveQuery = {},\n requestOptions?: RequestOptions\n) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'DELETE',\n query: { dryRun }\n }, requestOptions).then(() => undefined)\n );\n\nconst _create = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, data: any, requestOptions?: RequestOptions) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'POST',\n body: data\n }, requestOptions)\n );\n\nconst _update = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n data: any,\n { ignoreMissingProperties, dryRun = false }: UpdateQuery = {},\n requestOptions?: RequestOptions\n) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'PUT',\n body: data,\n query: {\n ignoreMissingProperties:\n ignoreMissingProperties ?? cfg?.ignoreMissingProperties ?? globalConfig?.ignoreMissingProperties,\n dryRun\n }\n }, requestOptions)\n );\n\nconst _generic = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n method: RequestPayloadMethod,\n endpoint: string,\n payload?: GenericQuery<any, any>,\n forceBlob?: boolean,\n requestOptions?: RequestOptions\n) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n }, requestOptions)\n);\n";
|
|
144
|
+
|
|
145
|
+
var unique = "const _unique = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, query?: UniqueQuery, requestOptions?: RequestOptions) =>\n wrapResponse(() => raw({ ...cfg, multiRequest: false }, endpoint, { query }, requestOptions));\n";
|
|
148
146
|
|
|
149
|
-
var types = "export type DeepPartial<T> = T extends object
|
|
147
|
+
var types = "export type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>;\n }\n : T;\n\nexport type Sort<T> = {\n [K in keyof T]?: {\n [V in keyof T]?: V extends K\n ? T[V] extends Array<infer U> | undefined\n ? U extends object\n ? Sort<U>\n : never\n : T[V] extends object | undefined\n ? Sort<T[V]>\n : 'asc' | 'desc'\n : never;\n };\n}[keyof T];\n\nexport type CustomAttributeFilter = {\n [K in number]:| string | number | boolean | { id: string } | { entityName: string; entityId: string };\n};\n\nexport type QuerySelect<T> = {\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\n ? QuerySelect<U> | boolean\n : T[P] extends Record<any, any> | undefined\n ? QuerySelect<T[P]> | boolean\n : boolean;\n};\n\nexport type Select<T, Q extends QuerySelect<T> | undefined> =\n Q extends QuerySelect<T>\n ? {\n // Filter out excluded properties beforehand\n [P in keyof T as Q[P] extends boolean ? P : Q[P] extends object ? P : never]: // Property\n Q[P] extends true\n ? T[P]\n : // Array\n T[P] extends Array<infer U>\n ? Select<U, Q[P] & QuerySelect<any>>[]\n : // Object\n T[P] extends Record<any, any>\n ? Select<T[P], Q[P] & QuerySelect<any>>\n : never;\n }\n : undefined;\n\nexport type MapKeys<T, S extends Record<keyof T, string>> = {\n [K in keyof T as S[K]]: T[K];\n};\n\nexport type ValueOf<T> = T[keyof T];\n\nexport type Pagination = {\n page: number;\n pageSize: number;\n};\n\nexport type UniqueQuery = {\n serializeNulls?: boolean;\n};\n\nexport type SomeQueryReturn<E, R, P> = {\n entities: E[];\n references?: R;\n properties?: P[];\n};\n\nexport type GenericQuery<P, B> = {\n params?: P;\n body?: B;\n};\n\nexport type UpdateQuery = {\n ignoreMissingProperties?: boolean;\n dryRun?: boolean;\n};\n\nexport type RemoveQuery = {\n dryRun?: boolean;\n};\n\nexport type WEntityPropertyMeta =\n | {\n type: 'string';\n format?: 'decimal' | 'html' | 'email' | 'password';\n maxLength?: number;\n pattern?: string;\n entity?: WEntity;\n service?: WService;\n }\n | {\n type: 'integer';\n format: 'int32' | 'int64' | 'duration' | 'date' | 'timestamp';\n }\n | { type: 'array'; format: 'reference'; entity: WEntity; service?: WService }\n | { type: 'array'; format: 'reference'; enum: WEnum }\n | { type: 'array'; format: 'string' }\n | { type: 'number'; format: 'double' }\n | { type: 'reference'; entity: WEntity }\n | { type: 'reference'; enum: WEnum }\n | { type: 'boolean' }\n | { type: 'object' };\n";
|
|
148
|
+
|
|
149
|
+
var utils = "const flattenSelect = (obj: Select<any, any> = {}): string[] => {\n const entries: string[] = [];\n\n for (const [prop, value] of Object.entries(obj)) {\n if (typeof value === 'object' && value) {\n entries.push(...flattenSelect(value).map((v) => `${prop}.${v}`));\n } else if (value) {\n entries.push(prop);\n }\n }\n\n return entries;\n};\n\nexport const flattenSort = (obj: Sort<any>[] = []): { sort?: string } => {\n const flatten = (obj: Sort<any>, base = ''): string | undefined => {\n const [key, value] = Object.entries(obj ?? {})[0] ?? [];\n\n if (key && value) {\n const path = base + key;\n\n if (typeof value === 'object') {\n return flatten(value, path ? `${path}.` : '');\n } else if (['asc', 'desc'].includes(value)) {\n return `${value === 'desc' ? '-' : ''}${path}`;\n }\n }\n\n return undefined;\n };\n\n const sorts = obj.map((v) => flatten(v)).filter(Boolean);\n return sorts.length ? { sort: sorts.join(',') } : {};\n};\n";
|
|
150
150
|
|
|
151
151
|
const resolveImports = (target) => {
|
|
152
152
|
const imports = [];
|
|
@@ -157,13 +157,13 @@ const resolveImports = (target) => {
|
|
|
157
157
|
};
|
|
158
158
|
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
|
|
159
159
|
const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
|
|
160
|
-
const generateBase = (target) => {
|
|
161
|
-
return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), types, root);
|
|
160
|
+
const generateBase = (target, apiVersion, options) => {
|
|
161
|
+
return generateStatements(resolveImports(target), `const apiVersion = ${apiVersion}`, resolveMappings(target), resolveBinaryClass(target), globalConfig, types, utils, root, options.useQueryLanguage ? queriesWithQueryLanguage : queriesWithFilter, options.generateUnique ? unique : '', multiRequest);
|
|
162
162
|
};
|
|
163
163
|
|
|
164
164
|
const transformKey = (s) => snakeCase(s).toUpperCase();
|
|
165
165
|
const generateEnum = (name, values) => {
|
|
166
|
-
const props = indent(values.map(v => `${transformKey(v)} = ${generateString(v)}`).join(',\n'));
|
|
166
|
+
const props = indent(values.map((v) => `${transformKey(v)} = ${generateString(v)}`).join(',\n'));
|
|
167
167
|
return `export enum ${name} {\n${props}\n}`;
|
|
168
168
|
};
|
|
169
169
|
|
|
@@ -194,19 +194,26 @@ const isResponseObject = (v) => {
|
|
|
194
194
|
const isNonArraySchemaObject = (v) => {
|
|
195
195
|
return isObject(v) && ['string', 'undefined'].includes(typeof v.type);
|
|
196
196
|
};
|
|
197
|
+
const isFilterPathsSchemaObject = (v) => {
|
|
198
|
+
return isObject(v) && v.type === 'object' && isObject(v['x-weclapp-filterPaths']);
|
|
199
|
+
};
|
|
200
|
+
const isFilterPropertySchemaObject = (v) => {
|
|
201
|
+
return isObject(v) && v.type === 'object' && isObject(v['x-weclapp-filterProperties']);
|
|
202
|
+
};
|
|
197
203
|
const isRelatedEntitySchema = (v) => {
|
|
198
204
|
return isObject(v) && isNonArraySchemaObject(v) && 'x-weclapp' in v && isObject(v['x-weclapp']);
|
|
199
205
|
};
|
|
200
206
|
|
|
201
207
|
const generateEnums = (schemas) => {
|
|
202
208
|
const enums = new Map();
|
|
203
|
-
for (const [
|
|
209
|
+
for (const [schemaName, schema] of schemas) {
|
|
204
210
|
if (isEnumSchemaObject(schema)) {
|
|
205
|
-
const
|
|
206
|
-
if (!enums.has(
|
|
207
|
-
enums.set(
|
|
211
|
+
const enumName = loosePascalCase(schemaName);
|
|
212
|
+
if (!enums.has(enumName)) {
|
|
213
|
+
enums.set(enumName, {
|
|
214
|
+
name: enumName,
|
|
208
215
|
properties: schema.enum,
|
|
209
|
-
source: generateEnum(
|
|
216
|
+
source: generateEnum(enumName, schema.enum)
|
|
210
217
|
});
|
|
211
218
|
}
|
|
212
219
|
}
|
|
@@ -218,16 +225,13 @@ const concat = (strings, separator = ', ', maxLength = 80) => {
|
|
|
218
225
|
const joined = strings.join(separator);
|
|
219
226
|
if (joined.length > maxLength) {
|
|
220
227
|
const length = strings.length - 1;
|
|
221
|
-
return `\n${indent(strings
|
|
222
|
-
.map((value, index) => index === length ? value : `${(value + separator).trim()}\n`)
|
|
223
|
-
.join(''))}\n`;
|
|
228
|
+
return `\n${indent(strings.map((value, index) => (index === length ? value : `${(value + separator).trim()}\n`)).join(''))}\n`;
|
|
224
229
|
}
|
|
225
230
|
else {
|
|
226
231
|
return joined;
|
|
227
232
|
}
|
|
228
233
|
};
|
|
229
234
|
|
|
230
|
-
/* eslint-disable no-use-before-define */
|
|
231
235
|
const createReferenceType = (value) => ({
|
|
232
236
|
type: 'reference',
|
|
233
237
|
toString: () => loosePascalCase(value)
|
|
@@ -238,26 +242,28 @@ const createRawType = (value) => ({
|
|
|
238
242
|
});
|
|
239
243
|
const createArrayType = (value) => ({
|
|
240
244
|
type: 'array',
|
|
241
|
-
toString: () =>
|
|
245
|
+
toString: () => `(${value.toString()})[]`
|
|
242
246
|
});
|
|
243
247
|
const createTupleType = (value) => ({
|
|
244
248
|
type: 'tuple',
|
|
245
|
-
toString: () => concat([...new Set(value.map(v => typeof v === 'string' ? `'${v}'` : v.toString()))], ' | ')
|
|
249
|
+
toString: () => concat([...new Set(value.map((v) => (typeof v === 'string' ? `'${v}'` : v.toString())))], ' | ')
|
|
246
250
|
});
|
|
247
251
|
const createObjectType = (value, required = []) => ({
|
|
248
252
|
type: 'object',
|
|
249
253
|
isFullyOptional: () => {
|
|
250
|
-
return !required.length &&
|
|
251
|
-
.
|
|
252
|
-
|
|
254
|
+
return (!required.length &&
|
|
255
|
+
Object.values(value)
|
|
256
|
+
.filter((v) => v?.type === 'object')
|
|
257
|
+
.every((v) => v.isFullyOptional()));
|
|
253
258
|
},
|
|
254
259
|
toString: (propagateOptionalProperties = false) => {
|
|
255
260
|
const properties = Object.entries(value)
|
|
256
|
-
.filter(v => v[1])
|
|
257
|
-
.map(v => {
|
|
261
|
+
.filter((v) => v[1])
|
|
262
|
+
.map((v) => {
|
|
258
263
|
const name = v[0];
|
|
259
264
|
const value = v[1];
|
|
260
|
-
const isRequired = required.includes(name) ||
|
|
265
|
+
const isRequired = required.includes(name) ||
|
|
266
|
+
(value.type === 'object' && !value.isFullyOptional() && propagateOptionalProperties);
|
|
261
267
|
return `${name + (isRequired ? '' : '?')}: ${value.toString()};`;
|
|
262
268
|
});
|
|
263
269
|
return properties.length ? `{\n${indent(properties.join('\n'))}\n}` : '{}';
|
|
@@ -286,8 +292,7 @@ const convertToTypeScriptType = (schema, property) => {
|
|
|
286
292
|
return createRawType('boolean');
|
|
287
293
|
case 'object': {
|
|
288
294
|
const { properties = {}, required = [] } = schema;
|
|
289
|
-
return createObjectType(Object.fromEntries(Object.entries(properties)
|
|
290
|
-
.map(v => [v[0], convertToTypeScriptType(v[1])])), required);
|
|
295
|
+
return createObjectType(Object.fromEntries(Object.entries(properties).map((v) => [v[0], convertToTypeScriptType(v[1])])), required);
|
|
291
296
|
}
|
|
292
297
|
case 'array':
|
|
293
298
|
return createArrayType(convertToTypeScriptType(schema.items, property));
|
|
@@ -308,14 +313,19 @@ const setEntityEnumProperty = (enums, prop, meta) => {
|
|
|
308
313
|
}
|
|
309
314
|
};
|
|
310
315
|
const extractPropertyMetaData = (enums, meta, prop) => {
|
|
311
|
-
const result = {
|
|
316
|
+
const result = {
|
|
317
|
+
service: meta.service,
|
|
318
|
+
entity: meta.entity
|
|
319
|
+
};
|
|
312
320
|
if (isReferenceObject(prop)) {
|
|
313
321
|
setEntityEnumProperty(enums, prop, result);
|
|
314
322
|
result.type = 'reference';
|
|
315
323
|
return result;
|
|
316
324
|
}
|
|
317
|
-
result.format = prop.format;
|
|
318
325
|
result.type = prop.type;
|
|
326
|
+
result.format = prop.format;
|
|
327
|
+
result.maxLength = prop.maxLength;
|
|
328
|
+
result.pattern = prop.pattern;
|
|
319
329
|
if (isArraySchemaObject(prop)) {
|
|
320
330
|
if (isReferenceObject(prop.items)) {
|
|
321
331
|
setEntityEnumProperty(enums, prop.items, result);
|
|
@@ -335,12 +345,12 @@ const generateType = (name, value) => {
|
|
|
335
345
|
return `export type ${name} = ${value.trim()};`;
|
|
336
346
|
};
|
|
337
347
|
|
|
338
|
-
const arrayify = (v) => Array.isArray(v) ? v : [v];
|
|
348
|
+
const arrayify = (v) => (Array.isArray(v) ? v : [v]);
|
|
339
349
|
|
|
340
350
|
const generateInterfaceProperties = (entries) => {
|
|
341
351
|
const properties = entries
|
|
342
|
-
.filter(v => v.type !== undefined)
|
|
343
|
-
.filter((value, index, array) => array.findIndex(v => v.name === value.name) === index)
|
|
352
|
+
.filter((v) => v.type !== undefined)
|
|
353
|
+
.filter((value, index, array) => array.findIndex((v) => v.name === value.name) === index)
|
|
344
354
|
.map(({ name, type, required, readonly, comment }) => {
|
|
345
355
|
const cmd = comment ? `${generateInlineComment(comment)}\n` : '';
|
|
346
356
|
const req = required ? '' : '?';
|
|
@@ -359,9 +369,20 @@ const generateInterface = (name, entries, extend) => {
|
|
|
359
369
|
const generateInterfaceType = (name, entries, extend) => {
|
|
360
370
|
const body = generateInterfaceProperties(entries);
|
|
361
371
|
const bases = extend ? arrayify(extend).join(' & ') : undefined;
|
|
362
|
-
|
|
372
|
+
let typeDefinition = '';
|
|
373
|
+
if (bases) {
|
|
374
|
+
typeDefinition = bases;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
typeDefinition = body;
|
|
378
|
+
}
|
|
379
|
+
if (bases && body !== '{}') {
|
|
380
|
+
typeDefinition += ` & ${body}`;
|
|
381
|
+
}
|
|
382
|
+
return generateType(name, typeDefinition);
|
|
363
383
|
};
|
|
364
384
|
|
|
385
|
+
const FILTER_PROPS_SUFFIX = 'Filter_Props';
|
|
365
386
|
const generateEntities = (schemas, enums) => {
|
|
366
387
|
const entities = new Map();
|
|
367
388
|
for (const [schemaName, schema] of schemas) {
|
|
@@ -369,73 +390,102 @@ const generateEntities = (schemas, enums) => {
|
|
|
369
390
|
if (isEnumSchemaObject(schema)) {
|
|
370
391
|
continue;
|
|
371
392
|
}
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
// Referenced entities and property-to-referenced-entity mapping
|
|
377
|
-
const referenceInterface = [];
|
|
378
|
-
const referenceMappingsInterface = [];
|
|
393
|
+
const entityInterfaceName = loosePascalCase(schemaName);
|
|
394
|
+
let parentEntityInterfaceName = undefined;
|
|
395
|
+
const entityInterfaceProperties = [];
|
|
396
|
+
const filterableInterfaceProperties = [];
|
|
379
397
|
const properties = new Map();
|
|
380
|
-
|
|
381
|
-
let extend = undefined;
|
|
382
|
-
const processProperties = (props = {}) => {
|
|
398
|
+
const processProperties = (props = {}, isXweclappFilterProp) => {
|
|
383
399
|
for (const [name, property] of Object.entries(props)) {
|
|
384
400
|
const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
401
|
+
const type = convertToTypeScriptType(property, name).toString();
|
|
402
|
+
const comment = isNonArraySchemaObject(property)
|
|
403
|
+
? property.deprecated
|
|
404
|
+
? '@deprecated will be removed.'
|
|
405
|
+
: property.format
|
|
406
|
+
? `format: ${property.format}`
|
|
407
|
+
: undefined
|
|
408
|
+
: undefined;
|
|
409
|
+
if (meta.filterable !== false) {
|
|
410
|
+
filterableInterfaceProperties.push({ name, type });
|
|
389
411
|
}
|
|
390
|
-
if (
|
|
391
|
-
|
|
412
|
+
if (!isXweclappFilterProp) {
|
|
413
|
+
entityInterfaceProperties.push({
|
|
414
|
+
name,
|
|
415
|
+
type,
|
|
416
|
+
comment,
|
|
417
|
+
required: meta.required,
|
|
418
|
+
filterable: meta.filterable ?? true,
|
|
419
|
+
readonly: !isReferenceObject(property) && property.readOnly
|
|
420
|
+
});
|
|
421
|
+
properties.set(name, extractPropertyMetaData(enums, meta, property));
|
|
392
422
|
}
|
|
393
|
-
const type = convertToTypeScriptType(property, name).toString();
|
|
394
|
-
const comment = isNonArraySchemaObject(property) ?
|
|
395
|
-
property.deprecated ? '@deprecated will be removed.' :
|
|
396
|
-
property.format ? `format: ${property.format}` :
|
|
397
|
-
undefined : undefined;
|
|
398
|
-
entityInterface.push({
|
|
399
|
-
name, type, comment,
|
|
400
|
-
required: meta.required,
|
|
401
|
-
readonly: !isReferenceObject(property) && property.readOnly
|
|
402
|
-
});
|
|
403
|
-
properties.set(name, extractPropertyMetaData(enums, meta, property));
|
|
404
423
|
}
|
|
405
424
|
};
|
|
406
425
|
if (schema.allOf?.length) {
|
|
407
426
|
for (const item of schema.allOf) {
|
|
408
427
|
if (isReferenceObject(item)) {
|
|
409
|
-
|
|
428
|
+
parentEntityInterfaceName = convertToTypeScriptType(item).toString();
|
|
410
429
|
}
|
|
411
430
|
else if (isObjectSchemaObject(item)) {
|
|
412
431
|
processProperties(item.properties);
|
|
432
|
+
if (isFilterPropertySchemaObject(item)) {
|
|
433
|
+
processProperties(item['x-weclapp-filterProperties'], true);
|
|
434
|
+
}
|
|
435
|
+
if (isFilterPathsSchemaObject(item)) {
|
|
436
|
+
const fPaths = item['x-weclapp-filterPaths'];
|
|
437
|
+
for (const path in fPaths) {
|
|
438
|
+
if (!path.includes('.')) {
|
|
439
|
+
filterableInterfaceProperties.push({ name: path, type: fPaths[path] });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
413
443
|
}
|
|
414
444
|
}
|
|
415
445
|
}
|
|
416
446
|
processProperties(schema.properties);
|
|
417
|
-
const source = generateStatements(generateInterface(entity, entityInterface, extend), generateInterface(`${entity}_References`, referenceInterface, extend ? [`${extend}_References`] : undefined), generateInterface(`${entity}_Mappings`, referenceMappingsInterface, extend ? [`${extend}_Mappings`] : undefined), generateInterfaceType(`${entity}_Filter`, filterInterface, extend ? [entity, `${extend}_Filter`] : undefined));
|
|
418
447
|
entities.set(schemaName, {
|
|
419
|
-
|
|
448
|
+
name: schemaName,
|
|
420
449
|
properties,
|
|
421
|
-
|
|
450
|
+
filterableInterfaceProperties: filterableInterfaceProperties.sort((propA, propB) => propA.name.localeCompare(propB.name)),
|
|
451
|
+
parentName: parentEntityInterfaceName ? camelCase(parentEntityInterfaceName) : undefined,
|
|
452
|
+
source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName))
|
|
422
453
|
});
|
|
423
454
|
}
|
|
424
455
|
return entities;
|
|
425
456
|
};
|
|
457
|
+
const generateEntityFilterProps = (entities, enums) => {
|
|
458
|
+
const entityFilterProps = new Map();
|
|
459
|
+
const transformFilterProps = (props) => {
|
|
460
|
+
return props.map((prop) => {
|
|
461
|
+
if (!prop.type || enums.has(prop.type) || prop.type === 'string' || prop.type === 'number' || prop.type === 'boolean' || prop.type.endsWith('[]')) {
|
|
462
|
+
return prop;
|
|
463
|
+
}
|
|
464
|
+
return { ...prop, type: `${pascalCase(prop.type)}_${FILTER_PROPS_SUFFIX}` };
|
|
465
|
+
});
|
|
466
|
+
};
|
|
467
|
+
entities.forEach((entity, name) => {
|
|
468
|
+
const entityFilterName = `${pascalCase(name)}_${FILTER_PROPS_SUFFIX}`;
|
|
469
|
+
const parentName = entity.parentName ? `${pascalCase(entity.parentName)}_${FILTER_PROPS_SUFFIX}` : undefined;
|
|
470
|
+
const filterableInterfaceProperties = transformFilterProps(entity.filterableInterfaceProperties);
|
|
471
|
+
entityFilterProps.set(entityFilterName, {
|
|
472
|
+
name: entityFilterName,
|
|
473
|
+
parentName,
|
|
474
|
+
source: generateStatements(generateInterface(entityFilterName, filterableInterfaceProperties, parentName))
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
return entityFilterProps;
|
|
478
|
+
};
|
|
426
479
|
|
|
427
480
|
/**
|
|
428
481
|
* Pluralizes a word, most of the time correct.
|
|
429
482
|
* @param s String to pluralize.
|
|
430
483
|
*/
|
|
431
484
|
const pluralize = (s) => {
|
|
432
|
-
return s.endsWith('s') ? s :
|
|
433
|
-
s.endsWith('y') ? `${s.slice(0, -1)}ies` :
|
|
434
|
-
`${s}s`;
|
|
485
|
+
return s.endsWith('s') ? s : s.endsWith('y') ? `${s.slice(0, -1)}ies` : `${s}s`;
|
|
435
486
|
};
|
|
436
487
|
|
|
437
|
-
|
|
438
|
-
const logger = new class {
|
|
488
|
+
const logger = new (class {
|
|
439
489
|
active = true;
|
|
440
490
|
warnings = 0;
|
|
441
491
|
errors = 0;
|
|
@@ -483,21 +533,24 @@ const logger = new class {
|
|
|
483
533
|
printSummary() {
|
|
484
534
|
const format = (v, name, fail, ok) => {
|
|
485
535
|
const color = v ? fail : ok;
|
|
486
|
-
return v === 0
|
|
487
|
-
|
|
536
|
+
return v === 0
|
|
537
|
+
? `${color('zero')} ${pluralize(name)}`
|
|
538
|
+
: v === 1
|
|
539
|
+
? `${color('one')} ${name}`
|
|
540
|
+
: `${color(v)} ${pluralize(name)}`;
|
|
488
541
|
};
|
|
489
542
|
const warnings = format(this.warnings, 'warning', chalk.yellowBright, chalk.greenBright);
|
|
490
543
|
const errors = format(this.errors, 'error', chalk.redBright, chalk.greenBright);
|
|
491
544
|
const info = `Finished with ${warnings} and ${errors}.`;
|
|
492
545
|
this[this.errors ? 'errorLn' : this.warnings ? 'warnLn' : 'successLn'](info);
|
|
493
546
|
}
|
|
494
|
-
};
|
|
547
|
+
})();
|
|
495
548
|
|
|
496
549
|
/**
|
|
497
|
-
* ROOT
|
|
498
|
-
* COUNT
|
|
499
|
-
* ENTITY
|
|
500
|
-
* SPECIAL_ROOT
|
|
550
|
+
* ROOT => /article
|
|
551
|
+
* COUNT => /article/count
|
|
552
|
+
* ENTITY => /article/{id}
|
|
553
|
+
* SPECIAL_ROOT => /article/generateImage
|
|
501
554
|
* SPECIAL_ENTITY => /article/id/{id}/generateImag
|
|
502
555
|
*/
|
|
503
556
|
var WeclappEndpointType;
|
|
@@ -509,23 +562,33 @@ var WeclappEndpointType;
|
|
|
509
562
|
WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
|
|
510
563
|
})(WeclappEndpointType || (WeclappEndpointType = {}));
|
|
511
564
|
const parseEndpointPath = (path) => {
|
|
512
|
-
const [,
|
|
513
|
-
if (!
|
|
565
|
+
const [, service, ...rest] = path.split('/');
|
|
566
|
+
if (!service) {
|
|
514
567
|
return undefined;
|
|
515
568
|
}
|
|
516
569
|
if (!rest.length) {
|
|
517
|
-
return { path,
|
|
570
|
+
return { path, service, type: WeclappEndpointType.ROOT };
|
|
518
571
|
}
|
|
519
572
|
else if (rest[0] === 'count') {
|
|
520
|
-
return { path,
|
|
573
|
+
return { path, service, type: WeclappEndpointType.COUNT };
|
|
521
574
|
}
|
|
522
575
|
else if (rest[0] === 'id') {
|
|
523
|
-
return rest.length === 2
|
|
524
|
-
{ path,
|
|
525
|
-
|
|
576
|
+
return rest.length === 2
|
|
577
|
+
? { path, service, type: WeclappEndpointType.ENTITY }
|
|
578
|
+
: {
|
|
579
|
+
path,
|
|
580
|
+
service,
|
|
581
|
+
method: rest[2],
|
|
582
|
+
type: WeclappEndpointType.GENERIC_ENTITY
|
|
583
|
+
};
|
|
526
584
|
}
|
|
527
585
|
else if (rest.length === 1) {
|
|
528
|
-
return {
|
|
586
|
+
return {
|
|
587
|
+
path,
|
|
588
|
+
service,
|
|
589
|
+
method: rest[1],
|
|
590
|
+
type: WeclappEndpointType.GENERIC_ROOT
|
|
591
|
+
};
|
|
529
592
|
}
|
|
530
593
|
return undefined;
|
|
531
594
|
};
|
|
@@ -547,48 +610,50 @@ const convertParametersToSchema = (parameters = []) => {
|
|
|
547
610
|
if (isParameterObject(param) && param.in === 'query') {
|
|
548
611
|
if (param.schema) {
|
|
549
612
|
properties.push([param.name, param.schema]);
|
|
550
|
-
|
|
613
|
+
if (param.required)
|
|
614
|
+
required.push(param.name);
|
|
551
615
|
}
|
|
552
616
|
}
|
|
553
617
|
}
|
|
554
618
|
return {
|
|
555
|
-
type: 'object',
|
|
619
|
+
type: 'object',
|
|
620
|
+
required,
|
|
556
621
|
properties: Object.fromEntries(properties)
|
|
557
622
|
};
|
|
558
623
|
};
|
|
559
624
|
|
|
560
|
-
const functionName$5 = 'count';
|
|
561
625
|
const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
const parameters = createObjectType({
|
|
569
|
-
params: convertToTypeScriptType(parameterSchema)
|
|
626
|
+
const functionName = 'count';
|
|
627
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
628
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
629
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
630
|
+
const parametersType = createObjectType({
|
|
631
|
+
params: convertToTypeScriptType(convertParametersToSchema(path.parameters))
|
|
570
632
|
});
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
params: [`query${parameters.isFullyOptional() ? '?' : ''}: CountQuery<${entityFilter}> & ${entityParameters}`],
|
|
633
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
634
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
635
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
|
|
636
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
637
|
+
type: functionTypeName,
|
|
638
|
+
params: [
|
|
639
|
+
`query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${path.parameters?.length ? ' & ' + parametersTypeName : ''}`, 'requestOptions?: RequestOptions'
|
|
640
|
+
],
|
|
580
641
|
returns: `${resolveResponseType(target)}<number>`
|
|
581
642
|
});
|
|
643
|
+
const functionSource = generateArrowFunction({
|
|
644
|
+
name: functionName,
|
|
645
|
+
signature: functionTypeName,
|
|
646
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
647
|
+
params: ['query', 'requestOptions?: RequestOptions']
|
|
648
|
+
});
|
|
582
649
|
return {
|
|
583
650
|
entity,
|
|
584
|
-
name: functionName
|
|
585
|
-
type: { name:
|
|
586
|
-
func: { name: functionName
|
|
651
|
+
name: functionName,
|
|
652
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
653
|
+
func: { name: functionName, source: functionSource },
|
|
587
654
|
interfaces: [
|
|
588
|
-
{
|
|
589
|
-
|
|
590
|
-
source: generateInterfaceFromObject(entityParameters, parameters, true)
|
|
591
|
-
}
|
|
655
|
+
...(path.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
|
|
656
|
+
{ name: filterTypeName, source: filterTypeSource }
|
|
592
657
|
]
|
|
593
658
|
};
|
|
594
659
|
};
|
|
@@ -610,39 +675,33 @@ const generateRequestBodyType = ({ requestBody }) => {
|
|
|
610
675
|
return generateBodyType(requestBody) ?? createRawType('unknown');
|
|
611
676
|
};
|
|
612
677
|
|
|
613
|
-
const resolveBodyType = ({ responses }) => Object.entries(responses)
|
|
614
|
-
.filter(v => v[0].startsWith('2'))[0]?.[1];
|
|
678
|
+
const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
|
|
615
679
|
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
|
|
616
680
|
|
|
617
|
-
const functionName$4 = 'create';
|
|
618
681
|
const generateCreateEndpoint = ({ target, path, endpoint }) => {
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
returns: `_${functionName$4}(cfg, ${generateString(endpoint.path)}, data)`,
|
|
625
|
-
params: ['data']
|
|
626
|
-
});
|
|
627
|
-
const interfaceSource = generateArrowFunctionType({
|
|
628
|
-
type: interfaceName,
|
|
629
|
-
params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`],
|
|
682
|
+
const functionName = 'create';
|
|
683
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
684
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
685
|
+
type: functionTypeName,
|
|
686
|
+
params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'requestOptions?: RequestOptions'],
|
|
630
687
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
631
688
|
});
|
|
689
|
+
const functionSource = generateArrowFunction({
|
|
690
|
+
name: functionName,
|
|
691
|
+
signature: functionTypeName,
|
|
692
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, data, requestOptions)`,
|
|
693
|
+
params: ['data', 'requestOptions?: RequestOptions']
|
|
694
|
+
});
|
|
632
695
|
return {
|
|
633
|
-
entity,
|
|
634
|
-
name: functionName
|
|
635
|
-
type: { name:
|
|
636
|
-
func: { name: functionName
|
|
696
|
+
entity: pascalCase(endpoint.service),
|
|
697
|
+
name: functionName,
|
|
698
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
699
|
+
func: { name: functionName, source: functionSource }
|
|
637
700
|
};
|
|
638
701
|
};
|
|
639
702
|
|
|
640
703
|
const generateGenericFunctionName = (path, suffix = '', prefix = '') => {
|
|
641
|
-
return camelCase(`${prefix}_` +
|
|
642
|
-
path
|
|
643
|
-
.replace(/.*\//, '')
|
|
644
|
-
.replace(/\W+/, '_')
|
|
645
|
-
.replace(/[_]+/, '_') + `_${suffix}`);
|
|
704
|
+
return camelCase(`${prefix}_` + path.replace(/.*\//, '').replace(/\W+/, '_').replace(/[_]+/, '_') + `_${suffix}`);
|
|
646
705
|
};
|
|
647
706
|
|
|
648
707
|
const insertPathPlaceholder = (path, record) => {
|
|
@@ -650,37 +709,33 @@ const insertPathPlaceholder = (path, record) => {
|
|
|
650
709
|
};
|
|
651
710
|
|
|
652
711
|
const wrapBody = (type, target) => {
|
|
653
|
-
return type.toString() === 'binary' ?
|
|
654
|
-
createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') :
|
|
655
|
-
type; // node-fetch returns a Blob as well
|
|
712
|
+
return type.toString() === 'binary' ? createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') : type; // node-fetch returns a Blob as well
|
|
656
713
|
};
|
|
657
714
|
const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
|
|
658
715
|
const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
|
|
659
|
-
const
|
|
660
|
-
const
|
|
661
|
-
const entityQuery = `${interfaceName}_Query`;
|
|
716
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
717
|
+
const entityQuery = `${functionTypeName}_Query`;
|
|
662
718
|
const hasId = endpoint.path.includes('{id}');
|
|
663
719
|
const params = createObjectType({
|
|
664
720
|
params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
|
|
665
721
|
body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
|
|
666
722
|
});
|
|
667
723
|
const responseBody = generateResponseBodyType(path);
|
|
668
|
-
const
|
|
724
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
725
|
+
type: functionTypeName,
|
|
726
|
+
params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`, 'requestOptions?: RequestOptions'],
|
|
727
|
+
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
|
|
728
|
+
});
|
|
669
729
|
const functionSource = generateArrowFunction({
|
|
670
730
|
name: functionName,
|
|
671
|
-
signature:
|
|
672
|
-
params: hasId ? ['id', 'query'] : ['query'],
|
|
673
|
-
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${
|
|
674
|
-
});
|
|
675
|
-
const interfaceSource = generateArrowFunctionType({
|
|
676
|
-
type: interfaceName,
|
|
677
|
-
params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
|
|
678
|
-
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
|
|
731
|
+
signature: functionTypeName,
|
|
732
|
+
params: hasId ? ['id', 'query', 'requestOptions?: RequestOptions'] : ['query', 'requestOptions?: RequestOptions'],
|
|
733
|
+
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${String(responseBody.toString() === 'binary')}, requestOptions)`
|
|
679
734
|
});
|
|
680
735
|
return {
|
|
681
|
-
entity,
|
|
736
|
+
entity: pascalCase(endpoint.service),
|
|
682
737
|
name: functionName,
|
|
683
|
-
type: { name:
|
|
738
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
684
739
|
func: { name: functionName, source: functionSource },
|
|
685
740
|
interfaces: [
|
|
686
741
|
{
|
|
@@ -691,35 +746,38 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
|
|
|
691
746
|
};
|
|
692
747
|
};
|
|
693
748
|
|
|
694
|
-
const functionName$3 = 'remove';
|
|
695
749
|
const generateRemoveEndpoint = ({ target, endpoint }) => {
|
|
696
|
-
const
|
|
697
|
-
const
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
|
|
702
|
-
params: ['id', 'options?: RemoveQuery']
|
|
703
|
-
});
|
|
704
|
-
const interfaceSource = generateArrowFunctionType({
|
|
705
|
-
type: interfaceName,
|
|
706
|
-
params: ['id: string', 'options?: RemoveQuery'],
|
|
750
|
+
const functionName = 'remove';
|
|
751
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
752
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
753
|
+
type: functionTypeName,
|
|
754
|
+
params: ['id: string', 'options?: RemoveQuery', 'requestOptions?: RequestOptions'],
|
|
707
755
|
returns: `${resolveResponseType(target)}<void>`
|
|
708
756
|
});
|
|
757
|
+
const functionSource = generateArrowFunction({
|
|
758
|
+
name: functionName,
|
|
759
|
+
signature: functionTypeName,
|
|
760
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options, requestOptions)`,
|
|
761
|
+
params: ['id', 'options?: RemoveQuery', 'requestOptions?: RequestOptions']
|
|
762
|
+
});
|
|
709
763
|
return {
|
|
710
|
-
entity,
|
|
711
|
-
name: functionName
|
|
712
|
-
type: { name:
|
|
713
|
-
func: { name: functionName
|
|
764
|
+
entity: pascalCase(endpoint.service),
|
|
765
|
+
name: functionName,
|
|
766
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
767
|
+
func: { name: functionName, source: functionSource }
|
|
714
768
|
};
|
|
715
769
|
};
|
|
716
770
|
|
|
717
|
-
const functionName$2 = 'some';
|
|
718
771
|
const excludedParameters = [
|
|
719
|
-
'page',
|
|
720
|
-
'
|
|
772
|
+
'page',
|
|
773
|
+
'pageSize',
|
|
774
|
+
'sort',
|
|
775
|
+
'serializeNulls',
|
|
776
|
+
'properties',
|
|
777
|
+
'includeReferencedEntities',
|
|
778
|
+
'additionalProperties'
|
|
721
779
|
];
|
|
722
|
-
const
|
|
780
|
+
const resolveAdditionalPropertiesSchema = (path) => {
|
|
723
781
|
const body = resolveBodyType(path);
|
|
724
782
|
if (isResponseObject(body)) {
|
|
725
783
|
const schema = body?.content?.['application/json']?.schema;
|
|
@@ -732,115 +790,161 @@ const resolveAdditionalProperties = (path) => {
|
|
|
732
790
|
}
|
|
733
791
|
return undefined;
|
|
734
792
|
};
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
793
|
+
const resolveReferences = (entity, entities) => {
|
|
794
|
+
const references = [];
|
|
795
|
+
const generatedEntity = entities.get(entity);
|
|
796
|
+
if (generatedEntity) {
|
|
797
|
+
for (const [property, propertyMetaData] of generatedEntity.properties) {
|
|
798
|
+
if (propertyMetaData.service) {
|
|
799
|
+
references.push({
|
|
800
|
+
name: property,
|
|
801
|
+
type: generateString(propertyMetaData.service),
|
|
802
|
+
required: true
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (generatedEntity.parentName) {
|
|
807
|
+
references.push(...resolveReferences(generatedEntity.parentName, entities));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return references;
|
|
811
|
+
};
|
|
812
|
+
const resolveReferencedEntities = (entity, entities) => {
|
|
813
|
+
const referencedEntities = [];
|
|
814
|
+
const generatedEntity = entities.get(entity);
|
|
815
|
+
if (generatedEntity) {
|
|
816
|
+
for (const [, propertyMetaData] of generatedEntity.properties) {
|
|
817
|
+
if (propertyMetaData.entity && propertyMetaData.service) {
|
|
818
|
+
referencedEntities.push({
|
|
819
|
+
name: propertyMetaData.service,
|
|
820
|
+
type: `${pascalCase(propertyMetaData.entity)}[]`,
|
|
821
|
+
required: true
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (generatedEntity.parentName) {
|
|
826
|
+
referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return referencedEntities;
|
|
830
|
+
};
|
|
831
|
+
const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) => {
|
|
832
|
+
const functionName = 'some';
|
|
833
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
834
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
835
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
836
|
+
const parameters = path.parameters?.filter((v) => (isParameterObject(v) ? !excludedParameters.includes(v.name) : false)) ?? [];
|
|
837
|
+
const parametersType = createObjectType({
|
|
838
|
+
params: convertToTypeScriptType(convertParametersToSchema(parameters))
|
|
753
839
|
});
|
|
754
|
-
const
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
840
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
841
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
842
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
|
|
843
|
+
const referencesTypeName = `${functionTypeName}_References`;
|
|
844
|
+
const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
|
|
845
|
+
const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
|
|
846
|
+
const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, 'string');
|
|
847
|
+
const queryTypeName = `${functionTypeName}_Query`;
|
|
848
|
+
const queryTypeSource = generateType(queryTypeName, `SomeQuery<${entity}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
|
|
849
|
+
const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
|
|
850
|
+
const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
|
|
851
|
+
const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
|
|
852
|
+
const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(path);
|
|
853
|
+
const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : '{}');
|
|
854
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
855
|
+
type: functionTypeName,
|
|
856
|
+
params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}, requestOptions?: RequestOptions`],
|
|
857
|
+
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
|
|
763
858
|
});
|
|
764
859
|
const functionSource = generateArrowFunction({
|
|
765
|
-
name: functionName
|
|
766
|
-
signature:
|
|
767
|
-
returns: `_${functionName
|
|
768
|
-
params: ['query']
|
|
860
|
+
name: functionName,
|
|
861
|
+
signature: functionTypeName,
|
|
862
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
863
|
+
params: ['query', 'requestOptions?: RequestOptions']
|
|
769
864
|
});
|
|
770
865
|
return {
|
|
771
866
|
entity,
|
|
772
|
-
name: functionName
|
|
773
|
-
type: { name:
|
|
774
|
-
func: { name: functionName
|
|
867
|
+
name: functionName,
|
|
868
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
869
|
+
func: { name: functionName, source: functionSource },
|
|
775
870
|
interfaces: [
|
|
776
|
-
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
871
|
+
{ name: parametersTypeName, source: parametersTypeSource },
|
|
872
|
+
{ name: filterTypeName, source: filterTypeSource },
|
|
873
|
+
{ name: referencesTypeName, source: referencesTypeSource },
|
|
874
|
+
{ name: additionalPropertyTypeName, source: additionalPropertyTypeSource },
|
|
875
|
+
{ name: queryTypeName, source: queryTypeSource },
|
|
876
|
+
{ name: referencedEntitiesTypeName, source: referencedEntitiesTypeSource },
|
|
877
|
+
{ name: additionalPropertiesTypeName, source: additionalPropertiesTypeSource }
|
|
780
878
|
]
|
|
781
879
|
};
|
|
782
880
|
};
|
|
783
881
|
|
|
784
|
-
const functionName$1 = 'unique';
|
|
785
882
|
const generateUniqueEndpoint = ({ target, path, endpoint }) => {
|
|
786
|
-
const
|
|
787
|
-
const
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
params: ['id', 'query'],
|
|
792
|
-
returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
|
|
793
|
-
});
|
|
794
|
-
const interfaceSource = generateArrowFunctionType({
|
|
795
|
-
type: interfaceName,
|
|
796
|
-
params: ['id: string', 'query?: Q'],
|
|
883
|
+
const functionName = 'unique';
|
|
884
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
885
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
886
|
+
type: functionTypeName,
|
|
887
|
+
params: ['id: string', 'query?: Q', 'requestOptions?: RequestOptions'],
|
|
797
888
|
generics: ['Q extends UniqueQuery'],
|
|
798
889
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
799
890
|
});
|
|
891
|
+
const functionSource = generateArrowFunction({
|
|
892
|
+
name: functionName,
|
|
893
|
+
signature: functionTypeName,
|
|
894
|
+
params: ['id', 'query', 'requestOptions?: RequestOptions'],
|
|
895
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, requestOptions?: RequestOptions)`
|
|
896
|
+
});
|
|
800
897
|
return {
|
|
801
|
-
entity,
|
|
802
|
-
name: functionName
|
|
803
|
-
type: { name:
|
|
804
|
-
func: { name: functionName
|
|
898
|
+
entity: pascalCase(endpoint.service),
|
|
899
|
+
name: functionName,
|
|
900
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
901
|
+
func: { name: functionName, source: functionSource }
|
|
805
902
|
};
|
|
806
903
|
};
|
|
807
904
|
|
|
808
|
-
const functionName = 'update';
|
|
809
905
|
const generateUpdateEndpoint = ({ target, path, endpoint }) => {
|
|
810
|
-
const
|
|
811
|
-
const
|
|
812
|
-
const
|
|
813
|
-
type:
|
|
814
|
-
params: ['id: string', `data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'options?: UpdateQuery'],
|
|
906
|
+
const functionName = 'update';
|
|
907
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
908
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
909
|
+
type: functionTypeName,
|
|
910
|
+
params: ['id: string', `data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'options?: UpdateQuery', 'requestOptions?: RequestOptions'],
|
|
815
911
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
816
912
|
});
|
|
817
913
|
const functionSource = generateArrowFunction({
|
|
818
914
|
name: functionName,
|
|
819
|
-
signature:
|
|
820
|
-
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options)`,
|
|
821
|
-
params: ['id', 'data', 'options']
|
|
915
|
+
signature: functionTypeName,
|
|
916
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options, requestOptions)`,
|
|
917
|
+
params: ['id', 'data', 'options', 'requestOptions?: RequestOptions']
|
|
822
918
|
});
|
|
823
919
|
return {
|
|
824
|
-
entity,
|
|
920
|
+
entity: pascalCase(endpoint.service),
|
|
825
921
|
name: functionName,
|
|
826
|
-
type: { name:
|
|
922
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
827
923
|
func: { name: functionName, source: functionSource }
|
|
828
924
|
};
|
|
829
925
|
};
|
|
830
926
|
|
|
831
|
-
const
|
|
927
|
+
const isMultiPartUploadPath = (path) => {
|
|
928
|
+
const [, entity, ...rest] = path.split('/');
|
|
929
|
+
return entity && rest.length === 2 && rest[1] === 'multipartUpload';
|
|
930
|
+
};
|
|
931
|
+
const parseEndpointsAndGroupByEntity = (paths) => {
|
|
832
932
|
const endpoints = new Map();
|
|
833
933
|
for (const [rawPath, path] of Object.entries(paths)) {
|
|
834
934
|
const endpoint = parseEndpointPath(rawPath);
|
|
835
935
|
if (!endpoint || !path) {
|
|
936
|
+
// Todo: Should be removed if sdk supports multi part upload.
|
|
937
|
+
if (isMultiPartUploadPath(rawPath)) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
836
940
|
logger.errorLn(`Failed to parse ${rawPath}`);
|
|
837
941
|
continue;
|
|
838
942
|
}
|
|
839
|
-
if (endpoints.has(endpoint.
|
|
840
|
-
endpoints.get(endpoint.
|
|
943
|
+
if (endpoints.has(endpoint.service)) {
|
|
944
|
+
endpoints.get(endpoint.service)?.push({ endpoint, path });
|
|
841
945
|
}
|
|
842
946
|
else {
|
|
843
|
-
endpoints.set(endpoint.
|
|
947
|
+
endpoints.set(endpoint.service, [{ endpoint, path }]);
|
|
844
948
|
}
|
|
845
949
|
}
|
|
846
950
|
return endpoints;
|
|
@@ -873,26 +977,35 @@ const generators = {
|
|
|
873
977
|
post: generateGenericEndpoint()
|
|
874
978
|
}
|
|
875
979
|
};
|
|
876
|
-
const generateServices = (
|
|
980
|
+
const generateServices = (paths, entities, aliases, options) => {
|
|
877
981
|
const services = new Map();
|
|
878
|
-
const
|
|
879
|
-
for (const [
|
|
880
|
-
const
|
|
881
|
-
const serviceTypeName = pascalCase(`${
|
|
882
|
-
// Service functions
|
|
982
|
+
const endpoints = parseEndpointsAndGroupByEntity(paths);
|
|
983
|
+
for (const [serviceName, paths] of endpoints) {
|
|
984
|
+
const serviceFnName = camelCase(`${serviceName}Service`);
|
|
985
|
+
const serviceTypeName = pascalCase(`${serviceName}Service`);
|
|
883
986
|
const functions = [];
|
|
884
987
|
for (const { path, endpoint } of paths) {
|
|
885
|
-
const
|
|
988
|
+
const generator = generators[endpoint.type];
|
|
886
989
|
for (const [method, config] of Object.entries(path)) {
|
|
887
|
-
if (method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique)
|
|
990
|
+
if ((method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique)
|
|
991
|
+
|| (method === 'post' && (endpoint.type === WeclappEndpointType.COUNT || endpoint.path.endsWith('query')))) {
|
|
992
|
+
// Skip unique endpoints if generateUnique option is not set or if POST is used for filter queries
|
|
888
993
|
continue;
|
|
889
994
|
}
|
|
890
|
-
|
|
995
|
+
const generatorFn = generator[method];
|
|
996
|
+
if (generatorFn) {
|
|
891
997
|
const path = config;
|
|
892
998
|
const target = options.target;
|
|
893
999
|
if (!path.deprecated || options.deprecated) {
|
|
894
1000
|
functions.push({
|
|
895
|
-
...
|
|
1001
|
+
...generatorFn({
|
|
1002
|
+
endpoint,
|
|
1003
|
+
method,
|
|
1004
|
+
target,
|
|
1005
|
+
path,
|
|
1006
|
+
entities,
|
|
1007
|
+
aliases
|
|
1008
|
+
}),
|
|
896
1009
|
path
|
|
897
1010
|
});
|
|
898
1011
|
}
|
|
@@ -905,26 +1018,28 @@ const generateServices = (doc, aliases, options) => {
|
|
|
905
1018
|
if (!functions.length) {
|
|
906
1019
|
continue;
|
|
907
1020
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
...functions.map(v => ({
|
|
1021
|
+
const serviceTypes = generateStatements(...functions.flatMap((v) => generateBlockComment(`${serviceTypeName} - ${pascalCase(v.name)}`, generateStatements(...[...(v.interfaces?.map((v) => v.source) ?? []), v.type.source]))), generateBlockComment(`${serviceTypeName}`, generateInterface(serviceTypeName, [
|
|
1022
|
+
...functions.map((v) => ({
|
|
911
1023
|
required: true,
|
|
912
1024
|
comment: v.path.deprecated ? '@deprecated' : undefined,
|
|
913
1025
|
name: v.func.name,
|
|
914
1026
|
type: v.type.name
|
|
915
1027
|
}))
|
|
916
|
-
]));
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1028
|
+
])));
|
|
1029
|
+
const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
|
|
1030
|
+
services.set(serviceName, {
|
|
1031
|
+
name: serviceName,
|
|
1032
|
+
serviceFnName,
|
|
1033
|
+
serviceTypeName,
|
|
1034
|
+
functions,
|
|
1035
|
+
source: generateStatements(serviceTypes, serviceFn),
|
|
1036
|
+
deprecated: functions.every((v) => v.path.deprecated)
|
|
1037
|
+
});
|
|
923
1038
|
}
|
|
924
1039
|
return services;
|
|
925
1040
|
};
|
|
926
1041
|
|
|
927
|
-
const
|
|
1042
|
+
const generateCustomValueServices = (entities, services) => {
|
|
928
1043
|
const customValueEntity = entities.get('customValue');
|
|
929
1044
|
const customValueEntities = [];
|
|
930
1045
|
if (!customValueEntity) {
|
|
@@ -932,7 +1047,7 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
932
1047
|
return '';
|
|
933
1048
|
}
|
|
934
1049
|
serviceLoop: for (const service of services) {
|
|
935
|
-
const someFunction = service.functions.find(v => v.name === 'some');
|
|
1050
|
+
const someFunction = service.functions.find((v) => v.name === 'some');
|
|
936
1051
|
if (!someFunction) {
|
|
937
1052
|
continue;
|
|
938
1053
|
}
|
|
@@ -945,55 +1060,57 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
945
1060
|
continue serviceLoop;
|
|
946
1061
|
}
|
|
947
1062
|
}
|
|
948
|
-
customValueEntities.push(service.
|
|
1063
|
+
customValueEntities.push(service.name);
|
|
949
1064
|
}
|
|
950
|
-
return
|
|
1065
|
+
return generateStatements(generateType('WCustomValueService', concat(generateStrings(customValueEntities), ' | ')), `export const wCustomValueServiceNames: WCustomValueService[] = [${concat(generateStrings(customValueEntities))}];`, `export const isWCustomValueService = (service: string | undefined): service is WCustomValueService =>\n${indent('wCustomValueServiceNames.includes(service as WCustomValueService);')}`);
|
|
951
1066
|
};
|
|
952
1067
|
|
|
953
1068
|
const generateObject = (properties) => {
|
|
954
1069
|
const body = [];
|
|
955
|
-
for (const { key, value } of properties) {
|
|
1070
|
+
for (const { key, value, comment } of properties) {
|
|
956
1071
|
if (value === undefined) {
|
|
957
1072
|
continue;
|
|
958
1073
|
}
|
|
959
1074
|
if (Array.isArray(value)) {
|
|
960
1075
|
const str = generateObject(value);
|
|
961
1076
|
if (str.length > 2) {
|
|
962
|
-
body.push(`${key}: ${str}`);
|
|
1077
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${str}`);
|
|
963
1078
|
}
|
|
964
1079
|
}
|
|
965
1080
|
else {
|
|
966
|
-
body.push(`${key}: ${String(value)}`);
|
|
1081
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${String(value)}`);
|
|
967
1082
|
}
|
|
968
1083
|
}
|
|
969
1084
|
return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
|
|
970
1085
|
};
|
|
971
1086
|
|
|
972
1087
|
const resolveInheritedEntities = (root, entities) => {
|
|
973
|
-
const parent = root.
|
|
1088
|
+
const parent = root.parentName ? entities.get(root.parentName) : undefined;
|
|
974
1089
|
return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
|
|
975
1090
|
};
|
|
976
|
-
const generatePropertyDescriptors = (entity, entities, services, options) => [
|
|
977
|
-
|
|
978
|
-
...entity.properties
|
|
979
|
-
].filter(([, meta]) => {
|
|
1091
|
+
const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
|
|
1092
|
+
.filter(([, meta]) => {
|
|
980
1093
|
// If we generate deprecated things we can skip the filtering
|
|
981
1094
|
if (options.deprecated) {
|
|
982
1095
|
return true;
|
|
983
1096
|
}
|
|
984
1097
|
// Check if corresponding service is deprecated and can be removed
|
|
985
|
-
const service = services.find(v => v.
|
|
1098
|
+
const service = services.find((v) => v.name === meta.service);
|
|
986
1099
|
return !meta.service || (service && !service.deprecated);
|
|
987
|
-
})
|
|
1100
|
+
})
|
|
1101
|
+
.map(([property, meta]) => ({
|
|
988
1102
|
key: property,
|
|
989
1103
|
value: Object.entries(meta).map(([key, value]) => ({
|
|
990
1104
|
key,
|
|
991
|
-
value: value ? generateString(value) : undefined
|
|
1105
|
+
value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
|
|
992
1106
|
}))
|
|
993
1107
|
}));
|
|
994
|
-
const
|
|
1108
|
+
const generateEntityProperties = (entities, aliases, services, options) => {
|
|
995
1109
|
const typeName = 'WEntityProperties';
|
|
996
|
-
const propertyMap = [
|
|
1110
|
+
const propertyMap = [
|
|
1111
|
+
...entities.entries(),
|
|
1112
|
+
...[...aliases.entries()].map(([service, type]) => [service, entities.get(camelCase(type))])
|
|
1113
|
+
].map(([entity, data]) => ({
|
|
997
1114
|
key: entity,
|
|
998
1115
|
value: generatePropertyDescriptors(data, entities, services, options)
|
|
999
1116
|
}));
|
|
@@ -1001,7 +1118,7 @@ const generateEntityPropertyMap = (entities, services, options) => {
|
|
|
1001
1118
|
};
|
|
1002
1119
|
|
|
1003
1120
|
const generateArray = (values) => {
|
|
1004
|
-
return `[${concat(values.map(v => generateString(String(v))))}]`;
|
|
1121
|
+
return `[${concat(values.map((v) => generateString(String(v))))}]`;
|
|
1005
1122
|
};
|
|
1006
1123
|
|
|
1007
1124
|
// Only functions matching this regex are included in the generation.
|
|
@@ -1014,16 +1131,17 @@ const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
|
|
|
1014
1131
|
*/
|
|
1015
1132
|
const generateGroupedServices = (services) => {
|
|
1016
1133
|
const entityDescriptors = new Map();
|
|
1017
|
-
for (const
|
|
1018
|
-
for (const
|
|
1019
|
-
if (!FILTER_REGEX.test(name)) {
|
|
1134
|
+
for (const service of services) {
|
|
1135
|
+
for (const fn of service.functions) {
|
|
1136
|
+
if (!FILTER_REGEX.test(fn.name)) {
|
|
1020
1137
|
continue;
|
|
1021
1138
|
}
|
|
1022
|
-
entityDescriptors.set(name, [
|
|
1023
|
-
...(entityDescriptors.get(name) ?? []),
|
|
1024
|
-
|
|
1139
|
+
entityDescriptors.set(fn.name, [
|
|
1140
|
+
...(entityDescriptors.get(fn.name) ?? []),
|
|
1141
|
+
{
|
|
1142
|
+
name: service.name,
|
|
1025
1143
|
required: true,
|
|
1026
|
-
type: `${pascalCase(
|
|
1144
|
+
type: `${pascalCase(service.name)}Service_${pascalCase(fn.name)}`
|
|
1027
1145
|
}
|
|
1028
1146
|
]);
|
|
1029
1147
|
}
|
|
@@ -1036,123 +1154,86 @@ const generateGroupedServices = (services) => {
|
|
|
1036
1154
|
const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
|
|
1037
1155
|
typeGuards.push(`export const is${service} = ${guard}`);
|
|
1038
1156
|
}
|
|
1039
|
-
return [
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
const value = generateArray(props.map(v => v.name));
|
|
1046
|
-
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1047
|
-
}),
|
|
1048
|
-
generateBlockComment('Type guards for service classes.', generateStatements(...typeGuards))
|
|
1049
|
-
];
|
|
1157
|
+
return generateStatements(...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)), ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)), ...descriptors.map(([name, props]) => {
|
|
1158
|
+
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1159
|
+
const type = pascalCase(`WServiceWith_${name}`);
|
|
1160
|
+
const value = generateArray(props.map((v) => v.name));
|
|
1161
|
+
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1162
|
+
}), ...typeGuards);
|
|
1050
1163
|
};
|
|
1051
1164
|
|
|
1052
|
-
const
|
|
1053
|
-
const
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
name: entity,
|
|
1067
|
-
type: pascalCase(entity),
|
|
1068
|
-
required: true
|
|
1069
|
-
})),
|
|
1070
|
-
...services.map(service => {
|
|
1071
|
-
const alias = aliases.get(service.entity);
|
|
1072
|
-
return {
|
|
1073
|
-
name: service.entity,
|
|
1074
|
-
type: alias ?? 'never',
|
|
1075
|
-
required: true,
|
|
1076
|
-
comment: alias ? undefined : 'no response defined or inlined'
|
|
1077
|
-
};
|
|
1078
|
-
})
|
|
1079
|
-
];
|
|
1080
|
-
const createMappingType = (type, prefix) => type !== 'never' ? `${type}_${prefix}` : type;
|
|
1081
|
-
const entitiesList = generateInterface('WEntities', entityInterfaces);
|
|
1082
|
-
const entityReferences = generateInterface('WEntityReferences', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'References') })));
|
|
1083
|
-
const entityMappings = generateInterface('WEntityMappings', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Mappings') })));
|
|
1084
|
-
const entityFilter = generateInterface('WEntityFilters', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Filter') })));
|
|
1165
|
+
const generateMaps = (enums, entities, services, aliases, options) => {
|
|
1166
|
+
const enumInstances = `export const wEnums = ${generateObject([...enums.keys()].map((v) => ({ key: v, value: v })))};`;
|
|
1167
|
+
const entityNames = `export const wEntityNames: WEntity[] = ${generateArray([...entities.keys()])};`;
|
|
1168
|
+
const generatedServices = [...services.values()];
|
|
1169
|
+
const serviceInstances = `export const wServices = ${generateObject(generatedServices.map((v) => ({
|
|
1170
|
+
key: v.name,
|
|
1171
|
+
value: `${v.serviceFnName}()`,
|
|
1172
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1173
|
+
})))};`;
|
|
1174
|
+
const serviceFactories = `export const wServiceFactories = ${generateObject(generatedServices.map((v) => ({
|
|
1175
|
+
key: v.name,
|
|
1176
|
+
value: v.serviceFnName,
|
|
1177
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1178
|
+
})))};`;
|
|
1085
1179
|
return {
|
|
1086
1180
|
source: generateStatements(
|
|
1087
|
-
/*
|
|
1088
|
-
|
|
1089
|
-
/*
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
Where [serviceName] is the name of an endpoint (e.g. for /article its 'article')
|
|
1102
|
-
|
|
1103
|
-
Where [entityInterfaceName] is
|
|
1104
|
-
- the underlying type for this entity
|
|
1105
|
-
- the type for what is returned by the api
|
|
1106
|
-
`, entitiesList),
|
|
1107
|
-
/* type-ofs and types */
|
|
1108
|
-
generateType('WServices', 'typeof wServices'), generateType('WServiceFactories', 'typeof wServiceFactories'), generateType('WService', 'keyof WServices'), generateType('WEntity', 'keyof WEntities'), generateType('WEnums', 'typeof wEnums'), generateType('WEnum', 'keyof WEnums'),
|
|
1109
|
-
/* Utilities. */
|
|
1110
|
-
generateCustomValueUtilities(entities, services),
|
|
1111
|
-
/* All functions grouped by service supporting it */
|
|
1112
|
-
...generateGroupedServices(services))
|
|
1181
|
+
/* Enums */
|
|
1182
|
+
generateInterface('WEnums', [...enums.keys()].map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
|
|
1183
|
+
/* Entities */
|
|
1184
|
+
generateInterface('WEntities', [
|
|
1185
|
+
...[...entities.keys()].map((name) => ({ name, type: loosePascalCase(name), required: true })),
|
|
1186
|
+
...[...aliases.entries()].map(([name, type]) => ({ name, type, required: true }))
|
|
1187
|
+
].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
|
|
1188
|
+
/* Services */
|
|
1189
|
+
serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
|
|
1190
|
+
/* Service Utils */
|
|
1191
|
+
generateGroupedServices(generatedServices), generateCustomValueServices(entities, generatedServices),
|
|
1192
|
+
/* Entity Properties (Runtime Meta Infos) */
|
|
1193
|
+
generateEntityProperties(entities, aliases, generatedServices, options))
|
|
1113
1194
|
};
|
|
1114
1195
|
};
|
|
1115
1196
|
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
const extractSchemas = (doc) => {
|
|
1119
|
-
const schemas = new Map();
|
|
1197
|
+
const parseReferencedEntityType = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
|
|
1198
|
+
const extractServiceAliases = (doc, schemas) => {
|
|
1120
1199
|
const aliases = new Map();
|
|
1121
|
-
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
|
|
1122
|
-
if (!isReferenceObject(schema)) {
|
|
1123
|
-
schemas.set(name, schema);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Referenced schemas in responses, in some case the response from the root endpoint
|
|
1128
|
-
* refers to a schema with a different name
|
|
1129
|
-
*/
|
|
1130
1200
|
for (const [path, methods] of Object.entries(doc.paths)) {
|
|
1131
1201
|
const parsed = parseEndpointPath(path);
|
|
1132
|
-
if (!parsed || schemas.has(parsed.
|
|
1202
|
+
if (!parsed || !methods || parsed.type !== WeclappEndpointType.ROOT || schemas.has(parsed.service)) {
|
|
1133
1203
|
continue;
|
|
1134
1204
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1205
|
+
const body = methods[OpenAPIV3.HttpMethods.GET]?.responses['200'];
|
|
1206
|
+
if (isResponseObject(body) && body.content?.['application/json']) {
|
|
1207
|
+
const responseSchema = body.content['application/json'].schema;
|
|
1208
|
+
if (!responseSchema || isReferenceObject(responseSchema)) {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
const resultSchema = responseSchema.properties?.result;
|
|
1212
|
+
if (!resultSchema) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
if (isReferenceObject(resultSchema)) {
|
|
1216
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultSchema));
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
if (isArraySchemaObject(resultSchema)) {
|
|
1220
|
+
const resultItemSchema = resultSchema.items;
|
|
1221
|
+
if (isReferenceObject(resultItemSchema)) {
|
|
1222
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultItemSchema));
|
|
1152
1223
|
}
|
|
1153
1224
|
}
|
|
1154
1225
|
}
|
|
1155
1226
|
}
|
|
1227
|
+
return aliases;
|
|
1228
|
+
};
|
|
1229
|
+
const extractSchemas = (doc) => {
|
|
1230
|
+
const schemas = new Map();
|
|
1231
|
+
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
|
|
1232
|
+
if (!isReferenceObject(schema)) {
|
|
1233
|
+
schemas.set(name, schema);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
const aliases = extractServiceAliases(doc, schemas);
|
|
1156
1237
|
return { schemas, aliases };
|
|
1157
1238
|
};
|
|
1158
1239
|
|
|
@@ -1160,14 +1241,10 @@ const generate = (doc, options) => {
|
|
|
1160
1241
|
const { schemas, aliases } = extractSchemas(doc);
|
|
1161
1242
|
const enums = generateEnums(schemas);
|
|
1162
1243
|
const entities = generateEntities(schemas, enums);
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
options,
|
|
1168
|
-
entities,
|
|
1169
|
-
aliases
|
|
1170
|
-
}).source));
|
|
1244
|
+
const entityFilterProps = generateEntityFilterProps(entities, enums);
|
|
1245
|
+
const services = generateServices(doc.paths, entities, aliases, options);
|
|
1246
|
+
const maps = generateMaps(enums, entities, services, aliases, options);
|
|
1247
|
+
return generateStatements(generateBase(options.target, doc.info.version, options), generateBlockComment('ENUMS', generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment('ENTITY FILTER PROPS', generateStatements(...[...entityFilterProps.values()].map((v) => v.source))), generateBlockComment('ENTITIES', generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment('SERVICES', generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment('MAPS', generateStatements(maps.source)));
|
|
1171
1248
|
};
|
|
1172
1249
|
|
|
1173
1250
|
const hash = (content, algorithm = 'sha256') => {
|
|
@@ -1183,12 +1260,13 @@ const hash = (content, algorithm = 'sha256') => {
|
|
|
1183
1260
|
|
|
1184
1261
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
1185
1262
|
const cli = async () => {
|
|
1186
|
-
const
|
|
1263
|
+
const version = pkg.version;
|
|
1187
1264
|
const { argv } = yargs(hideBin(process.argv))
|
|
1188
1265
|
.scriptName('build-weclapp-sdk')
|
|
1189
1266
|
.usage('Usage: $0 <source> [flags]')
|
|
1190
1267
|
.version(version)
|
|
1191
1268
|
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1269
|
+
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1192
1270
|
.example('$0 xxx.weclapp.com --key ...', 'Generate the SDK based on the openapi file from the given weclapp instance')
|
|
1193
1271
|
.help('h')
|
|
1194
1272
|
.alias('v', 'version')
|
|
@@ -1228,36 +1306,47 @@ const cli = async () => {
|
|
|
1228
1306
|
type: 'string',
|
|
1229
1307
|
choices: ['browser', 'browser.rx', 'node', 'node.rx']
|
|
1230
1308
|
})
|
|
1231
|
-
.option('
|
|
1232
|
-
|
|
1233
|
-
describe: 'Include deprecated functions and services',
|
|
1309
|
+
.option('use-query-language', {
|
|
1310
|
+
describe: 'Generate the new where property for some and count queries',
|
|
1234
1311
|
type: 'boolean'
|
|
1312
|
+
})
|
|
1313
|
+
.option('apiVersion', {
|
|
1314
|
+
describe: 'Specify the api version (only needed when not using a local file)',
|
|
1315
|
+
type: 'string'
|
|
1235
1316
|
})
|
|
1236
1317
|
.epilog(`Copyright ${new Date().getFullYear()} weclapp GmbH`);
|
|
1237
1318
|
if (argv.fromEnv) {
|
|
1238
1319
|
config();
|
|
1239
1320
|
}
|
|
1240
1321
|
const { WECLAPP_API_KEY, WECLAPP_BACKEND_URL } = process.env;
|
|
1241
|
-
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, _: [src = WECLAPP_BACKEND_URL] } = argv;
|
|
1322
|
+
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, apiVersion, _: [src = WECLAPP_BACKEND_URL] } = argv;
|
|
1242
1323
|
const options = {
|
|
1243
1324
|
deprecated,
|
|
1244
1325
|
generateUnique: argv.generateUnique ?? false,
|
|
1245
|
-
target: argv.target ?? Target.BROWSER_PROMISES
|
|
1326
|
+
target: argv.target ?? Target.BROWSER_PROMISES,
|
|
1327
|
+
useQueryLanguage: argv.useQueryLanguage ?? false
|
|
1246
1328
|
};
|
|
1247
|
-
if (typeof src === 'number') {
|
|
1248
|
-
return Promise.reject('Expected string as command');
|
|
1329
|
+
if (!src || typeof src === 'number') {
|
|
1330
|
+
return Promise.reject(new Error('Expected string as command'));
|
|
1249
1331
|
}
|
|
1250
1332
|
if (!Object.values(Target).includes(options.target)) {
|
|
1251
1333
|
logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(', ')}`);
|
|
1252
|
-
return Promise.reject();
|
|
1334
|
+
return Promise.reject(new Error());
|
|
1253
1335
|
}
|
|
1254
1336
|
if (await stat(src).catch(() => false)) {
|
|
1255
|
-
logger.infoLn(`Source is a file
|
|
1337
|
+
logger.infoLn(`Source is a file`);
|
|
1256
1338
|
const content = JSON.parse(await readFile(src, 'utf-8'));
|
|
1257
1339
|
return { cache, content, options };
|
|
1258
1340
|
}
|
|
1341
|
+
logger.infoLn(`Source is a URL`);
|
|
1342
|
+
if (!key) {
|
|
1343
|
+
return Promise.reject(new Error('API key is missing'));
|
|
1344
|
+
}
|
|
1345
|
+
if (!apiVersion) {
|
|
1346
|
+
return Promise.reject(new Error('API version is missing'));
|
|
1347
|
+
}
|
|
1259
1348
|
const url = new URL(src.startsWith('http') ? src : `https://${src}`);
|
|
1260
|
-
url.pathname =
|
|
1349
|
+
url.pathname = `/webapp/api/${apiVersion}/meta/openapi.json`;
|
|
1261
1350
|
if (query?.length) {
|
|
1262
1351
|
for (const param of query.split(',')) {
|
|
1263
1352
|
const [name, value] = param.split('=');
|
|
@@ -1265,11 +1354,11 @@ const cli = async () => {
|
|
|
1265
1354
|
}
|
|
1266
1355
|
}
|
|
1267
1356
|
const content = await fetch(url.toString(), {
|
|
1268
|
-
headers: {
|
|
1269
|
-
}).then(res => res.ok ? res.json() : undefined);
|
|
1357
|
+
headers: { Accept: 'application/json', AuthenticationToken: key }
|
|
1358
|
+
}).then((res) => (res.ok ? res.json() : undefined));
|
|
1270
1359
|
if (!content) {
|
|
1271
1360
|
logger.errorLn(`Couldn't fetch file ${url.toString()} `);
|
|
1272
|
-
return Promise.reject();
|
|
1361
|
+
return Promise.reject(new Error());
|
|
1273
1362
|
}
|
|
1274
1363
|
else {
|
|
1275
1364
|
logger.infoLn(`Use remote file: ${url.toString()}`);
|
|
@@ -1277,50 +1366,59 @@ const cli = async () => {
|
|
|
1277
1366
|
return { cache, content, options };
|
|
1278
1367
|
};
|
|
1279
1368
|
|
|
1280
|
-
const
|
|
1281
|
-
const
|
|
1369
|
+
const workingDir = resolve(currentDirname(), './sdk');
|
|
1370
|
+
const cacheDir = resolve(currentDirname(), './.cache');
|
|
1282
1371
|
void (async () => {
|
|
1283
1372
|
const start = process.hrtime.bigint();
|
|
1284
|
-
const { default: { version } } = await import('../package.json', { assert: { type: 'json' } });
|
|
1285
1373
|
const { content: doc, cache: useCache, options } = await cli();
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
const dist = (...paths) => resolve(workingDirectory, ...paths);
|
|
1290
|
-
const tmp = async (...paths) => {
|
|
1291
|
-
const fullPath = resolve(cacheDir, ...paths);
|
|
1292
|
-
await mkdir(dirname(fullPath), { recursive: true }).catch(() => null);
|
|
1374
|
+
const workingDirPath = async (...paths) => {
|
|
1375
|
+
const fullPath = resolve(workingDir, ...paths);
|
|
1376
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
1293
1377
|
return fullPath;
|
|
1294
1378
|
};
|
|
1379
|
+
// Resolve cache dir and key
|
|
1380
|
+
const cacheKey = hash([pkg.version, JSON.stringify(doc), JSON.stringify(options)]).slice(-8);
|
|
1381
|
+
const cachedSdkDir = resolve(cacheDir, cacheKey);
|
|
1382
|
+
// Remove old SDK
|
|
1383
|
+
await rm(workingDir, { recursive: true, force: true });
|
|
1295
1384
|
if (useCache) {
|
|
1296
1385
|
logger.infoLn(`Cache ID: ${cacheKey}`);
|
|
1297
1386
|
}
|
|
1298
|
-
if (useCache && await stat(
|
|
1299
|
-
|
|
1387
|
+
if (useCache && (await stat(cachedSdkDir).catch(() => false))) {
|
|
1388
|
+
// Copy cached SDK to working dir
|
|
1389
|
+
logger.successLn(`Cache match! (${cachedSdkDir})`);
|
|
1390
|
+
await cp(cachedSdkDir, workingDir, { recursive: true });
|
|
1300
1391
|
}
|
|
1301
1392
|
else {
|
|
1302
|
-
//
|
|
1303
|
-
await writeFile(await
|
|
1393
|
+
// Write openapi.json file
|
|
1394
|
+
await writeFile(await workingDirPath('openapi.json'), JSON.stringify(doc, null, 2));
|
|
1304
1395
|
logger.infoLn(`Generate sdk (target: ${options.target})`);
|
|
1305
|
-
// Generate
|
|
1396
|
+
// Generate and write SDK (index.ts)
|
|
1306
1397
|
const sdk = generate(doc, options);
|
|
1307
|
-
await writeFile(await
|
|
1308
|
-
// Bundle
|
|
1398
|
+
await writeFile(await workingDirPath('src', 'index.ts'), sdk.trim() + '\n');
|
|
1399
|
+
// Bundle and write SDK
|
|
1309
1400
|
logger.infoLn('Bundle... (this may take some time)');
|
|
1310
|
-
await bundle(
|
|
1311
|
-
// Remove
|
|
1312
|
-
await
|
|
1401
|
+
await bundle(workingDir, options.target);
|
|
1402
|
+
// Remove index.ts (only bundle is required)
|
|
1403
|
+
await rm(await workingDirPath('src'), { recursive: true, force: true });
|
|
1404
|
+
if (useCache) {
|
|
1405
|
+
// Copy SDK to cache
|
|
1406
|
+
logger.successLn(`Caching SDK: (${cachedSdkDir})`);
|
|
1407
|
+
await mkdir(cachedSdkDir, { recursive: true });
|
|
1408
|
+
await cp(workingDir, cachedSdkDir, { recursive: true });
|
|
1409
|
+
}
|
|
1313
1410
|
}
|
|
1314
|
-
// Copy bundled SDK
|
|
1315
|
-
await cp(cacheDir, workingDirectory, { recursive: true });
|
|
1316
1411
|
// Print job summary
|
|
1317
1412
|
const duration = (process.hrtime.bigint() - start) / 1000000n;
|
|
1318
1413
|
logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
|
|
1319
1414
|
logger.printSummary();
|
|
1320
|
-
})()
|
|
1415
|
+
})()
|
|
1416
|
+
.catch((error) => {
|
|
1321
1417
|
logger.errorLn(`Fatal error:`);
|
|
1322
1418
|
/* eslint-disable no-console */
|
|
1323
1419
|
console.error(error);
|
|
1324
|
-
})
|
|
1325
|
-
|
|
1420
|
+
})
|
|
1421
|
+
.finally(() => {
|
|
1422
|
+
if (logger.errors)
|
|
1423
|
+
process.exit(1);
|
|
1326
1424
|
});
|