@weclapp/sdk 2.0.0-dev.4 → 2.0.0-dev.42
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 +581 -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,108 @@ 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 ||
|
|
462
|
+
enums.has(prop.type) ||
|
|
463
|
+
prop.type === 'string' ||
|
|
464
|
+
prop.type === 'number' ||
|
|
465
|
+
prop.type === 'boolean' ||
|
|
466
|
+
prop.type.endsWith('[]') ||
|
|
467
|
+
prop.type === '{}') {
|
|
468
|
+
return prop;
|
|
469
|
+
}
|
|
470
|
+
return { ...prop, type: `${pascalCase(prop.type)}_${FILTER_PROPS_SUFFIX}` };
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
entities.forEach((entity, name) => {
|
|
474
|
+
const entityFilterName = `${pascalCase(name)}_${FILTER_PROPS_SUFFIX}`;
|
|
475
|
+
const parentName = entity.parentName ? `${pascalCase(entity.parentName)}_${FILTER_PROPS_SUFFIX}` : undefined;
|
|
476
|
+
const filterableInterfaceProperties = transformFilterProps(entity.filterableInterfaceProperties);
|
|
477
|
+
entityFilterProps.set(entityFilterName, {
|
|
478
|
+
name: entityFilterName,
|
|
479
|
+
parentName,
|
|
480
|
+
source: generateStatements(generateInterface(entityFilterName, filterableInterfaceProperties, parentName))
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
return entityFilterProps;
|
|
484
|
+
};
|
|
426
485
|
|
|
427
486
|
/**
|
|
428
487
|
* Pluralizes a word, most of the time correct.
|
|
429
488
|
* @param s String to pluralize.
|
|
430
489
|
*/
|
|
431
490
|
const pluralize = (s) => {
|
|
432
|
-
return s.endsWith('s') ? s :
|
|
433
|
-
s.endsWith('y') ? `${s.slice(0, -1)}ies` :
|
|
434
|
-
`${s}s`;
|
|
491
|
+
return s.endsWith('s') ? s : s.endsWith('y') ? `${s.slice(0, -1)}ies` : `${s}s`;
|
|
435
492
|
};
|
|
436
493
|
|
|
437
|
-
|
|
438
|
-
const logger = new class {
|
|
494
|
+
const logger = new (class {
|
|
439
495
|
active = true;
|
|
440
496
|
warnings = 0;
|
|
441
497
|
errors = 0;
|
|
@@ -483,21 +539,24 @@ const logger = new class {
|
|
|
483
539
|
printSummary() {
|
|
484
540
|
const format = (v, name, fail, ok) => {
|
|
485
541
|
const color = v ? fail : ok;
|
|
486
|
-
return v === 0
|
|
487
|
-
|
|
542
|
+
return v === 0
|
|
543
|
+
? `${color('zero')} ${pluralize(name)}`
|
|
544
|
+
: v === 1
|
|
545
|
+
? `${color('one')} ${name}`
|
|
546
|
+
: `${color(v)} ${pluralize(name)}`;
|
|
488
547
|
};
|
|
489
548
|
const warnings = format(this.warnings, 'warning', chalk.yellowBright, chalk.greenBright);
|
|
490
549
|
const errors = format(this.errors, 'error', chalk.redBright, chalk.greenBright);
|
|
491
550
|
const info = `Finished with ${warnings} and ${errors}.`;
|
|
492
551
|
this[this.errors ? 'errorLn' : this.warnings ? 'warnLn' : 'successLn'](info);
|
|
493
552
|
}
|
|
494
|
-
};
|
|
553
|
+
})();
|
|
495
554
|
|
|
496
555
|
/**
|
|
497
|
-
* ROOT
|
|
498
|
-
* COUNT
|
|
499
|
-
* ENTITY
|
|
500
|
-
* SPECIAL_ROOT
|
|
556
|
+
* ROOT => /article
|
|
557
|
+
* COUNT => /article/count
|
|
558
|
+
* ENTITY => /article/{id}
|
|
559
|
+
* SPECIAL_ROOT => /article/generateImage
|
|
501
560
|
* SPECIAL_ENTITY => /article/id/{id}/generateImag
|
|
502
561
|
*/
|
|
503
562
|
var WeclappEndpointType;
|
|
@@ -509,23 +568,33 @@ var WeclappEndpointType;
|
|
|
509
568
|
WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
|
|
510
569
|
})(WeclappEndpointType || (WeclappEndpointType = {}));
|
|
511
570
|
const parseEndpointPath = (path) => {
|
|
512
|
-
const [,
|
|
513
|
-
if (!
|
|
571
|
+
const [, service, ...rest] = path.split('/');
|
|
572
|
+
if (!service) {
|
|
514
573
|
return undefined;
|
|
515
574
|
}
|
|
516
575
|
if (!rest.length) {
|
|
517
|
-
return { path,
|
|
576
|
+
return { path, service, type: WeclappEndpointType.ROOT };
|
|
518
577
|
}
|
|
519
578
|
else if (rest[0] === 'count') {
|
|
520
|
-
return { path,
|
|
579
|
+
return { path, service, type: WeclappEndpointType.COUNT };
|
|
521
580
|
}
|
|
522
581
|
else if (rest[0] === 'id') {
|
|
523
|
-
return rest.length === 2
|
|
524
|
-
{ path,
|
|
525
|
-
|
|
582
|
+
return rest.length === 2
|
|
583
|
+
? { path, service, type: WeclappEndpointType.ENTITY }
|
|
584
|
+
: {
|
|
585
|
+
path,
|
|
586
|
+
service,
|
|
587
|
+
method: rest[2],
|
|
588
|
+
type: WeclappEndpointType.GENERIC_ENTITY
|
|
589
|
+
};
|
|
526
590
|
}
|
|
527
591
|
else if (rest.length === 1) {
|
|
528
|
-
return {
|
|
592
|
+
return {
|
|
593
|
+
path,
|
|
594
|
+
service,
|
|
595
|
+
method: rest[1],
|
|
596
|
+
type: WeclappEndpointType.GENERIC_ROOT
|
|
597
|
+
};
|
|
529
598
|
}
|
|
530
599
|
return undefined;
|
|
531
600
|
};
|
|
@@ -547,48 +616,51 @@ const convertParametersToSchema = (parameters = []) => {
|
|
|
547
616
|
if (isParameterObject(param) && param.in === 'query') {
|
|
548
617
|
if (param.schema) {
|
|
549
618
|
properties.push([param.name, param.schema]);
|
|
550
|
-
|
|
619
|
+
if (param.required)
|
|
620
|
+
required.push(param.name);
|
|
551
621
|
}
|
|
552
622
|
}
|
|
553
623
|
}
|
|
554
624
|
return {
|
|
555
|
-
type: 'object',
|
|
625
|
+
type: 'object',
|
|
626
|
+
required,
|
|
556
627
|
properties: Object.fromEntries(properties)
|
|
557
628
|
};
|
|
558
629
|
};
|
|
559
630
|
|
|
560
|
-
const functionName$5 = 'count';
|
|
561
631
|
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)
|
|
632
|
+
const functionName = 'count';
|
|
633
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
634
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
635
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
636
|
+
const parametersType = createObjectType({
|
|
637
|
+
params: convertToTypeScriptType(convertParametersToSchema(path.parameters))
|
|
570
638
|
});
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
639
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
640
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
641
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
|
|
642
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
643
|
+
type: functionTypeName,
|
|
644
|
+
params: [
|
|
645
|
+
`query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${path.parameters?.length ? ' & ' + parametersTypeName : ''}`,
|
|
646
|
+
'requestOptions?: RequestOptions'
|
|
647
|
+
],
|
|
580
648
|
returns: `${resolveResponseType(target)}<number>`
|
|
581
649
|
});
|
|
650
|
+
const functionSource = generateArrowFunction({
|
|
651
|
+
name: functionName,
|
|
652
|
+
signature: functionTypeName,
|
|
653
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
654
|
+
params: ['query', 'requestOptions?: RequestOptions']
|
|
655
|
+
});
|
|
582
656
|
return {
|
|
583
657
|
entity,
|
|
584
|
-
name: functionName
|
|
585
|
-
type: { name:
|
|
586
|
-
func: { name: functionName
|
|
658
|
+
name: functionName,
|
|
659
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
660
|
+
func: { name: functionName, source: functionSource },
|
|
587
661
|
interfaces: [
|
|
588
|
-
{
|
|
589
|
-
|
|
590
|
-
source: generateInterfaceFromObject(entityParameters, parameters, true)
|
|
591
|
-
}
|
|
662
|
+
...(path.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
|
|
663
|
+
{ name: filterTypeName, source: filterTypeSource }
|
|
592
664
|
]
|
|
593
665
|
};
|
|
594
666
|
};
|
|
@@ -610,39 +682,33 @@ const generateRequestBodyType = ({ requestBody }) => {
|
|
|
610
682
|
return generateBodyType(requestBody) ?? createRawType('unknown');
|
|
611
683
|
};
|
|
612
684
|
|
|
613
|
-
const resolveBodyType = ({ responses }) => Object.entries(responses)
|
|
614
|
-
.filter(v => v[0].startsWith('2'))[0]?.[1];
|
|
685
|
+
const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
|
|
615
686
|
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
|
|
616
687
|
|
|
617
|
-
const functionName$4 = 'create';
|
|
618
688
|
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()}>`],
|
|
689
|
+
const functionName = 'create';
|
|
690
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
691
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
692
|
+
type: functionTypeName,
|
|
693
|
+
params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'requestOptions?: RequestOptions'],
|
|
630
694
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
631
695
|
});
|
|
696
|
+
const functionSource = generateArrowFunction({
|
|
697
|
+
name: functionName,
|
|
698
|
+
signature: functionTypeName,
|
|
699
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, data, requestOptions)`,
|
|
700
|
+
params: ['data', 'requestOptions?: RequestOptions']
|
|
701
|
+
});
|
|
632
702
|
return {
|
|
633
|
-
entity,
|
|
634
|
-
name: functionName
|
|
635
|
-
type: { name:
|
|
636
|
-
func: { name: functionName
|
|
703
|
+
entity: pascalCase(endpoint.service),
|
|
704
|
+
name: functionName,
|
|
705
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
706
|
+
func: { name: functionName, source: functionSource }
|
|
637
707
|
};
|
|
638
708
|
};
|
|
639
709
|
|
|
640
710
|
const generateGenericFunctionName = (path, suffix = '', prefix = '') => {
|
|
641
|
-
return camelCase(`${prefix}_` +
|
|
642
|
-
path
|
|
643
|
-
.replace(/.*\//, '')
|
|
644
|
-
.replace(/\W+/, '_')
|
|
645
|
-
.replace(/[_]+/, '_') + `_${suffix}`);
|
|
711
|
+
return camelCase(`${prefix}_` + path.replace(/.*\//, '').replace(/\W+/, '_').replace(/[_]+/, '_') + `_${suffix}`);
|
|
646
712
|
};
|
|
647
713
|
|
|
648
714
|
const insertPathPlaceholder = (path, record) => {
|
|
@@ -650,37 +716,37 @@ const insertPathPlaceholder = (path, record) => {
|
|
|
650
716
|
};
|
|
651
717
|
|
|
652
718
|
const wrapBody = (type, target) => {
|
|
653
|
-
return type.toString() === 'binary' ?
|
|
654
|
-
createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') :
|
|
655
|
-
type; // node-fetch returns a Blob as well
|
|
719
|
+
return type.toString() === 'binary' ? createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') : type; // node-fetch returns a Blob as well
|
|
656
720
|
};
|
|
657
721
|
const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
|
|
658
722
|
const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
|
|
659
|
-
const
|
|
660
|
-
const
|
|
661
|
-
const entityQuery = `${interfaceName}_Query`;
|
|
723
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
724
|
+
const entityQuery = `${functionTypeName}_Query`;
|
|
662
725
|
const hasId = endpoint.path.includes('{id}');
|
|
663
726
|
const params = createObjectType({
|
|
664
727
|
params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
|
|
665
728
|
body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
|
|
666
729
|
});
|
|
667
730
|
const responseBody = generateResponseBodyType(path);
|
|
668
|
-
const
|
|
731
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
732
|
+
type: functionTypeName,
|
|
733
|
+
params: [
|
|
734
|
+
...(hasId ? ['id: string'] : []),
|
|
735
|
+
`query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`,
|
|
736
|
+
'requestOptions?: RequestOptions'
|
|
737
|
+
],
|
|
738
|
+
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
|
|
739
|
+
});
|
|
669
740
|
const functionSource = generateArrowFunction({
|
|
670
741
|
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()}>`
|
|
742
|
+
signature: functionTypeName,
|
|
743
|
+
params: hasId ? ['id', 'query', 'requestOptions?: RequestOptions'] : ['query', 'requestOptions?: RequestOptions'],
|
|
744
|
+
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${String(responseBody.toString() === 'binary')}, requestOptions)`
|
|
679
745
|
});
|
|
680
746
|
return {
|
|
681
|
-
entity,
|
|
747
|
+
entity: pascalCase(endpoint.service),
|
|
682
748
|
name: functionName,
|
|
683
|
-
type: { name:
|
|
749
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
684
750
|
func: { name: functionName, source: functionSource },
|
|
685
751
|
interfaces: [
|
|
686
752
|
{
|
|
@@ -691,35 +757,38 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
|
|
|
691
757
|
};
|
|
692
758
|
};
|
|
693
759
|
|
|
694
|
-
const functionName$3 = 'remove';
|
|
695
760
|
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'],
|
|
761
|
+
const functionName = 'remove';
|
|
762
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
763
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
764
|
+
type: functionTypeName,
|
|
765
|
+
params: ['id: string', 'options?: RemoveQuery', 'requestOptions?: RequestOptions'],
|
|
707
766
|
returns: `${resolveResponseType(target)}<void>`
|
|
708
767
|
});
|
|
768
|
+
const functionSource = generateArrowFunction({
|
|
769
|
+
name: functionName,
|
|
770
|
+
signature: functionTypeName,
|
|
771
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options, requestOptions)`,
|
|
772
|
+
params: ['id', 'options?: RemoveQuery', 'requestOptions?: RequestOptions']
|
|
773
|
+
});
|
|
709
774
|
return {
|
|
710
|
-
entity,
|
|
711
|
-
name: functionName
|
|
712
|
-
type: { name:
|
|
713
|
-
func: { name: functionName
|
|
775
|
+
entity: pascalCase(endpoint.service),
|
|
776
|
+
name: functionName,
|
|
777
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
778
|
+
func: { name: functionName, source: functionSource }
|
|
714
779
|
};
|
|
715
780
|
};
|
|
716
781
|
|
|
717
|
-
const functionName$2 = 'some';
|
|
718
782
|
const excludedParameters = [
|
|
719
|
-
'page',
|
|
720
|
-
'
|
|
783
|
+
'page',
|
|
784
|
+
'pageSize',
|
|
785
|
+
'sort',
|
|
786
|
+
'serializeNulls',
|
|
787
|
+
'properties',
|
|
788
|
+
'includeReferencedEntities',
|
|
789
|
+
'additionalProperties'
|
|
721
790
|
];
|
|
722
|
-
const
|
|
791
|
+
const resolveAdditionalPropertiesSchema = (path) => {
|
|
723
792
|
const body = resolveBodyType(path);
|
|
724
793
|
if (isResponseObject(body)) {
|
|
725
794
|
const schema = body?.content?.['application/json']?.schema;
|
|
@@ -732,115 +801,166 @@ const resolveAdditionalProperties = (path) => {
|
|
|
732
801
|
}
|
|
733
802
|
return undefined;
|
|
734
803
|
};
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
804
|
+
const resolveReferences = (entity, entities) => {
|
|
805
|
+
const references = [];
|
|
806
|
+
const generatedEntity = entities.get(entity);
|
|
807
|
+
if (generatedEntity) {
|
|
808
|
+
for (const [property, propertyMetaData] of generatedEntity.properties) {
|
|
809
|
+
if (propertyMetaData.service) {
|
|
810
|
+
references.push({
|
|
811
|
+
name: property,
|
|
812
|
+
type: generateString(propertyMetaData.service),
|
|
813
|
+
required: true
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (generatedEntity.parentName) {
|
|
818
|
+
references.push(...resolveReferences(generatedEntity.parentName, entities));
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return references;
|
|
822
|
+
};
|
|
823
|
+
const resolveReferencedEntities = (entity, entities) => {
|
|
824
|
+
const referencedEntities = [];
|
|
825
|
+
const generatedEntity = entities.get(entity);
|
|
826
|
+
if (generatedEntity) {
|
|
827
|
+
for (const [, propertyMetaData] of generatedEntity.properties) {
|
|
828
|
+
if (propertyMetaData.entity && propertyMetaData.service) {
|
|
829
|
+
referencedEntities.push({
|
|
830
|
+
name: propertyMetaData.service,
|
|
831
|
+
type: `${pascalCase(propertyMetaData.entity)}[]`,
|
|
832
|
+
required: true
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (generatedEntity.parentName) {
|
|
837
|
+
referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return referencedEntities;
|
|
841
|
+
};
|
|
842
|
+
const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) => {
|
|
843
|
+
const functionName = 'some';
|
|
844
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
845
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
846
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
847
|
+
const parameters = path.parameters?.filter((v) => (isParameterObject(v) ? !excludedParameters.includes(v.name) : false)) ?? [];
|
|
848
|
+
const parametersType = createObjectType({
|
|
849
|
+
params: convertToTypeScriptType(convertParametersToSchema(parameters))
|
|
753
850
|
});
|
|
754
|
-
const
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
851
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
852
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
853
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
|
|
854
|
+
const referencesTypeName = `${functionTypeName}_References`;
|
|
855
|
+
const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
|
|
856
|
+
const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
|
|
857
|
+
const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, 'string');
|
|
858
|
+
const queryTypeName = `${functionTypeName}_Query`;
|
|
859
|
+
const queryTypeSource = generateType(queryTypeName, `SomeQuery<${entity}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
|
|
860
|
+
const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
|
|
861
|
+
const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
|
|
862
|
+
const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
|
|
863
|
+
const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(path);
|
|
864
|
+
const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : '{}');
|
|
865
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
866
|
+
type: functionTypeName,
|
|
867
|
+
params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}, requestOptions?: RequestOptions`],
|
|
868
|
+
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
|
|
763
869
|
});
|
|
764
870
|
const functionSource = generateArrowFunction({
|
|
765
|
-
name: functionName
|
|
766
|
-
signature:
|
|
767
|
-
returns: `_${functionName
|
|
768
|
-
params: ['query']
|
|
871
|
+
name: functionName,
|
|
872
|
+
signature: functionTypeName,
|
|
873
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
874
|
+
params: ['query', 'requestOptions?: RequestOptions']
|
|
769
875
|
});
|
|
770
876
|
return {
|
|
771
877
|
entity,
|
|
772
|
-
name: functionName
|
|
773
|
-
type: { name:
|
|
774
|
-
func: { name: functionName
|
|
878
|
+
name: functionName,
|
|
879
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
880
|
+
func: { name: functionName, source: functionSource },
|
|
775
881
|
interfaces: [
|
|
776
|
-
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
882
|
+
{ name: parametersTypeName, source: parametersTypeSource },
|
|
883
|
+
{ name: filterTypeName, source: filterTypeSource },
|
|
884
|
+
{ name: referencesTypeName, source: referencesTypeSource },
|
|
885
|
+
{ name: additionalPropertyTypeName, source: additionalPropertyTypeSource },
|
|
886
|
+
{ name: queryTypeName, source: queryTypeSource },
|
|
887
|
+
{ name: referencedEntitiesTypeName, source: referencedEntitiesTypeSource },
|
|
888
|
+
{ name: additionalPropertiesTypeName, source: additionalPropertiesTypeSource }
|
|
780
889
|
]
|
|
781
890
|
};
|
|
782
891
|
};
|
|
783
892
|
|
|
784
|
-
const functionName$1 = 'unique';
|
|
785
893
|
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'],
|
|
894
|
+
const functionName = 'unique';
|
|
895
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
896
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
897
|
+
type: functionTypeName,
|
|
898
|
+
params: ['id: string', 'query?: Q', 'requestOptions?: RequestOptions'],
|
|
797
899
|
generics: ['Q extends UniqueQuery'],
|
|
798
900
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
799
901
|
});
|
|
902
|
+
const functionSource = generateArrowFunction({
|
|
903
|
+
name: functionName,
|
|
904
|
+
signature: functionTypeName,
|
|
905
|
+
params: ['id', 'query', 'requestOptions?: RequestOptions'],
|
|
906
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, requestOptions?: RequestOptions)`
|
|
907
|
+
});
|
|
800
908
|
return {
|
|
801
|
-
entity,
|
|
802
|
-
name: functionName
|
|
803
|
-
type: { name:
|
|
804
|
-
func: { name: functionName
|
|
909
|
+
entity: pascalCase(endpoint.service),
|
|
910
|
+
name: functionName,
|
|
911
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
912
|
+
func: { name: functionName, source: functionSource }
|
|
805
913
|
};
|
|
806
914
|
};
|
|
807
915
|
|
|
808
|
-
const functionName = 'update';
|
|
809
916
|
const generateUpdateEndpoint = ({ target, path, endpoint }) => {
|
|
810
|
-
const
|
|
811
|
-
const
|
|
812
|
-
const
|
|
813
|
-
type:
|
|
814
|
-
params: [
|
|
917
|
+
const functionName = 'update';
|
|
918
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
919
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
920
|
+
type: functionTypeName,
|
|
921
|
+
params: [
|
|
922
|
+
'id: string',
|
|
923
|
+
`data: DeepPartial<${generateRequestBodyType(path).toString()}>`,
|
|
924
|
+
'options?: UpdateQuery',
|
|
925
|
+
'requestOptions?: RequestOptions'
|
|
926
|
+
],
|
|
815
927
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
816
928
|
});
|
|
817
929
|
const functionSource = generateArrowFunction({
|
|
818
930
|
name: functionName,
|
|
819
|
-
signature:
|
|
820
|
-
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options)`,
|
|
821
|
-
params: ['id', 'data', 'options']
|
|
931
|
+
signature: functionTypeName,
|
|
932
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options, requestOptions)`,
|
|
933
|
+
params: ['id', 'data', 'options', 'requestOptions?: RequestOptions']
|
|
822
934
|
});
|
|
823
935
|
return {
|
|
824
|
-
entity,
|
|
936
|
+
entity: pascalCase(endpoint.service),
|
|
825
937
|
name: functionName,
|
|
826
|
-
type: { name:
|
|
938
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
827
939
|
func: { name: functionName, source: functionSource }
|
|
828
940
|
};
|
|
829
941
|
};
|
|
830
942
|
|
|
831
|
-
const
|
|
943
|
+
const isMultiPartUploadPath = (path) => {
|
|
944
|
+
const [, entity, ...rest] = path.split('/');
|
|
945
|
+
return entity && rest.length === 2 && rest[1] === 'multipartUpload';
|
|
946
|
+
};
|
|
947
|
+
const parseEndpointsAndGroupByEntity = (paths) => {
|
|
832
948
|
const endpoints = new Map();
|
|
833
949
|
for (const [rawPath, path] of Object.entries(paths)) {
|
|
834
950
|
const endpoint = parseEndpointPath(rawPath);
|
|
835
951
|
if (!endpoint || !path) {
|
|
952
|
+
// Todo: Should be removed if sdk supports multi part upload.
|
|
953
|
+
if (isMultiPartUploadPath(rawPath)) {
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
836
956
|
logger.errorLn(`Failed to parse ${rawPath}`);
|
|
837
957
|
continue;
|
|
838
958
|
}
|
|
839
|
-
if (endpoints.has(endpoint.
|
|
840
|
-
endpoints.get(endpoint.
|
|
959
|
+
if (endpoints.has(endpoint.service)) {
|
|
960
|
+
endpoints.get(endpoint.service)?.push({ endpoint, path });
|
|
841
961
|
}
|
|
842
962
|
else {
|
|
843
|
-
endpoints.set(endpoint.
|
|
963
|
+
endpoints.set(endpoint.service, [{ endpoint, path }]);
|
|
844
964
|
}
|
|
845
965
|
}
|
|
846
966
|
return endpoints;
|
|
@@ -873,26 +993,35 @@ const generators = {
|
|
|
873
993
|
post: generateGenericEndpoint()
|
|
874
994
|
}
|
|
875
995
|
};
|
|
876
|
-
const generateServices = (
|
|
996
|
+
const generateServices = (paths, entities, aliases, options) => {
|
|
877
997
|
const services = new Map();
|
|
878
|
-
const
|
|
879
|
-
for (const [
|
|
880
|
-
const
|
|
881
|
-
const serviceTypeName = pascalCase(`${
|
|
882
|
-
// Service functions
|
|
998
|
+
const endpoints = parseEndpointsAndGroupByEntity(paths);
|
|
999
|
+
for (const [serviceName, paths] of endpoints) {
|
|
1000
|
+
const serviceFnName = camelCase(`${serviceName}Service`);
|
|
1001
|
+
const serviceTypeName = pascalCase(`${serviceName}Service`);
|
|
883
1002
|
const functions = [];
|
|
884
1003
|
for (const { path, endpoint } of paths) {
|
|
885
|
-
const
|
|
1004
|
+
const generator = generators[endpoint.type];
|
|
886
1005
|
for (const [method, config] of Object.entries(path)) {
|
|
887
|
-
if (method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique)
|
|
1006
|
+
if ((method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique) ||
|
|
1007
|
+
(method === 'post' && (endpoint.type === WeclappEndpointType.COUNT || endpoint.path.endsWith('query')))) {
|
|
1008
|
+
// Skip unique endpoints if generateUnique option is not set or if POST is used for filter queries
|
|
888
1009
|
continue;
|
|
889
1010
|
}
|
|
890
|
-
|
|
1011
|
+
const generatorFn = generator[method];
|
|
1012
|
+
if (generatorFn) {
|
|
891
1013
|
const path = config;
|
|
892
1014
|
const target = options.target;
|
|
893
1015
|
if (!path.deprecated || options.deprecated) {
|
|
894
1016
|
functions.push({
|
|
895
|
-
...
|
|
1017
|
+
...generatorFn({
|
|
1018
|
+
endpoint,
|
|
1019
|
+
method,
|
|
1020
|
+
target,
|
|
1021
|
+
path,
|
|
1022
|
+
entities,
|
|
1023
|
+
aliases
|
|
1024
|
+
}),
|
|
896
1025
|
path
|
|
897
1026
|
});
|
|
898
1027
|
}
|
|
@@ -905,26 +1034,28 @@ const generateServices = (doc, aliases, options) => {
|
|
|
905
1034
|
if (!functions.length) {
|
|
906
1035
|
continue;
|
|
907
1036
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
...functions.map(v => ({
|
|
1037
|
+
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, [
|
|
1038
|
+
...functions.map((v) => ({
|
|
911
1039
|
required: true,
|
|
912
1040
|
comment: v.path.deprecated ? '@deprecated' : undefined,
|
|
913
1041
|
name: v.func.name,
|
|
914
1042
|
type: v.type.name
|
|
915
1043
|
}))
|
|
916
|
-
]));
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1044
|
+
])));
|
|
1045
|
+
const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
|
|
1046
|
+
services.set(serviceName, {
|
|
1047
|
+
name: serviceName,
|
|
1048
|
+
serviceFnName,
|
|
1049
|
+
serviceTypeName,
|
|
1050
|
+
functions,
|
|
1051
|
+
source: generateStatements(serviceTypes, serviceFn),
|
|
1052
|
+
deprecated: functions.every((v) => v.path.deprecated)
|
|
1053
|
+
});
|
|
923
1054
|
}
|
|
924
1055
|
return services;
|
|
925
1056
|
};
|
|
926
1057
|
|
|
927
|
-
const
|
|
1058
|
+
const generateCustomValueServices = (entities, services) => {
|
|
928
1059
|
const customValueEntity = entities.get('customValue');
|
|
929
1060
|
const customValueEntities = [];
|
|
930
1061
|
if (!customValueEntity) {
|
|
@@ -932,7 +1063,7 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
932
1063
|
return '';
|
|
933
1064
|
}
|
|
934
1065
|
serviceLoop: for (const service of services) {
|
|
935
|
-
const someFunction = service.functions.find(v => v.name === 'some');
|
|
1066
|
+
const someFunction = service.functions.find((v) => v.name === 'some');
|
|
936
1067
|
if (!someFunction) {
|
|
937
1068
|
continue;
|
|
938
1069
|
}
|
|
@@ -945,55 +1076,57 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
945
1076
|
continue serviceLoop;
|
|
946
1077
|
}
|
|
947
1078
|
}
|
|
948
|
-
customValueEntities.push(service.
|
|
1079
|
+
customValueEntities.push(service.name);
|
|
949
1080
|
}
|
|
950
|
-
return
|
|
1081
|
+
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
1082
|
};
|
|
952
1083
|
|
|
953
1084
|
const generateObject = (properties) => {
|
|
954
1085
|
const body = [];
|
|
955
|
-
for (const { key, value } of properties) {
|
|
1086
|
+
for (const { key, value, comment } of properties) {
|
|
956
1087
|
if (value === undefined) {
|
|
957
1088
|
continue;
|
|
958
1089
|
}
|
|
959
1090
|
if (Array.isArray(value)) {
|
|
960
1091
|
const str = generateObject(value);
|
|
961
1092
|
if (str.length > 2) {
|
|
962
|
-
body.push(`${key}: ${str}`);
|
|
1093
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${str}`);
|
|
963
1094
|
}
|
|
964
1095
|
}
|
|
965
1096
|
else {
|
|
966
|
-
body.push(`${key}: ${String(value)}`);
|
|
1097
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${String(value)}`);
|
|
967
1098
|
}
|
|
968
1099
|
}
|
|
969
1100
|
return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
|
|
970
1101
|
};
|
|
971
1102
|
|
|
972
1103
|
const resolveInheritedEntities = (root, entities) => {
|
|
973
|
-
const parent = root.
|
|
1104
|
+
const parent = root.parentName ? entities.get(root.parentName) : undefined;
|
|
974
1105
|
return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
|
|
975
1106
|
};
|
|
976
|
-
const generatePropertyDescriptors = (entity, entities, services, options) => [
|
|
977
|
-
|
|
978
|
-
...entity.properties
|
|
979
|
-
].filter(([, meta]) => {
|
|
1107
|
+
const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
|
|
1108
|
+
.filter(([, meta]) => {
|
|
980
1109
|
// If we generate deprecated things we can skip the filtering
|
|
981
1110
|
if (options.deprecated) {
|
|
982
1111
|
return true;
|
|
983
1112
|
}
|
|
984
1113
|
// Check if corresponding service is deprecated and can be removed
|
|
985
|
-
const service = services.find(v => v.
|
|
1114
|
+
const service = services.find((v) => v.name === meta.service);
|
|
986
1115
|
return !meta.service || (service && !service.deprecated);
|
|
987
|
-
})
|
|
1116
|
+
})
|
|
1117
|
+
.map(([property, meta]) => ({
|
|
988
1118
|
key: property,
|
|
989
1119
|
value: Object.entries(meta).map(([key, value]) => ({
|
|
990
1120
|
key,
|
|
991
|
-
value: value ? generateString(value) : undefined
|
|
1121
|
+
value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
|
|
992
1122
|
}))
|
|
993
1123
|
}));
|
|
994
|
-
const
|
|
1124
|
+
const generateEntityProperties = (entities, aliases, services, options) => {
|
|
995
1125
|
const typeName = 'WEntityProperties';
|
|
996
|
-
const propertyMap = [
|
|
1126
|
+
const propertyMap = [
|
|
1127
|
+
...entities.entries(),
|
|
1128
|
+
...[...aliases.entries()].map(([service, type]) => [service, entities.get(camelCase(type))])
|
|
1129
|
+
].map(([entity, data]) => ({
|
|
997
1130
|
key: entity,
|
|
998
1131
|
value: generatePropertyDescriptors(data, entities, services, options)
|
|
999
1132
|
}));
|
|
@@ -1001,7 +1134,7 @@ const generateEntityPropertyMap = (entities, services, options) => {
|
|
|
1001
1134
|
};
|
|
1002
1135
|
|
|
1003
1136
|
const generateArray = (values) => {
|
|
1004
|
-
return `[${concat(values.map(v => generateString(String(v))))}]`;
|
|
1137
|
+
return `[${concat(values.map((v) => generateString(String(v))))}]`;
|
|
1005
1138
|
};
|
|
1006
1139
|
|
|
1007
1140
|
// Only functions matching this regex are included in the generation.
|
|
@@ -1014,16 +1147,17 @@ const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
|
|
|
1014
1147
|
*/
|
|
1015
1148
|
const generateGroupedServices = (services) => {
|
|
1016
1149
|
const entityDescriptors = new Map();
|
|
1017
|
-
for (const
|
|
1018
|
-
for (const
|
|
1019
|
-
if (!FILTER_REGEX.test(name)) {
|
|
1150
|
+
for (const service of services) {
|
|
1151
|
+
for (const fn of service.functions) {
|
|
1152
|
+
if (!FILTER_REGEX.test(fn.name)) {
|
|
1020
1153
|
continue;
|
|
1021
1154
|
}
|
|
1022
|
-
entityDescriptors.set(name, [
|
|
1023
|
-
...(entityDescriptors.get(name) ?? []),
|
|
1024
|
-
|
|
1155
|
+
entityDescriptors.set(fn.name, [
|
|
1156
|
+
...(entityDescriptors.get(fn.name) ?? []),
|
|
1157
|
+
{
|
|
1158
|
+
name: service.name,
|
|
1025
1159
|
required: true,
|
|
1026
|
-
type: `${pascalCase(
|
|
1160
|
+
type: `${pascalCase(service.name)}Service_${pascalCase(fn.name)}`
|
|
1027
1161
|
}
|
|
1028
1162
|
]);
|
|
1029
1163
|
}
|
|
@@ -1036,123 +1170,86 @@ const generateGroupedServices = (services) => {
|
|
|
1036
1170
|
const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
|
|
1037
1171
|
typeGuards.push(`export const is${service} = ${guard}`);
|
|
1038
1172
|
}
|
|
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
|
-
];
|
|
1173
|
+
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]) => {
|
|
1174
|
+
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1175
|
+
const type = pascalCase(`WServiceWith_${name}`);
|
|
1176
|
+
const value = generateArray(props.map((v) => v.name));
|
|
1177
|
+
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1178
|
+
}), ...typeGuards);
|
|
1050
1179
|
};
|
|
1051
1180
|
|
|
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') })));
|
|
1181
|
+
const generateMaps = (enums, entities, services, aliases, options) => {
|
|
1182
|
+
const enumInstances = `export const wEnums = ${generateObject([...enums.keys()].map((v) => ({ key: v, value: v })))};`;
|
|
1183
|
+
const entityNames = `export const wEntityNames: WEntity[] = ${generateArray([...entities.keys()])};`;
|
|
1184
|
+
const generatedServices = [...services.values()];
|
|
1185
|
+
const serviceInstances = `export const wServices = ${generateObject(generatedServices.map((v) => ({
|
|
1186
|
+
key: v.name,
|
|
1187
|
+
value: `${v.serviceFnName}()`,
|
|
1188
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1189
|
+
})))};`;
|
|
1190
|
+
const serviceFactories = `export const wServiceFactories = ${generateObject(generatedServices.map((v) => ({
|
|
1191
|
+
key: v.name,
|
|
1192
|
+
value: v.serviceFnName,
|
|
1193
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1194
|
+
})))};`;
|
|
1085
1195
|
return {
|
|
1086
1196
|
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))
|
|
1197
|
+
/* Enums */
|
|
1198
|
+
generateInterface('WEnums', [...enums.keys()].map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
|
|
1199
|
+
/* Entities */
|
|
1200
|
+
generateInterface('WEntities', [
|
|
1201
|
+
...[...entities.keys()].map((name) => ({ name, type: loosePascalCase(name), required: true })),
|
|
1202
|
+
...[...aliases.entries()].map(([name, type]) => ({ name, type, required: true }))
|
|
1203
|
+
].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
|
|
1204
|
+
/* Services */
|
|
1205
|
+
serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
|
|
1206
|
+
/* Service Utils */
|
|
1207
|
+
generateGroupedServices(generatedServices), generateCustomValueServices(entities, generatedServices),
|
|
1208
|
+
/* Entity Properties (Runtime Meta Infos) */
|
|
1209
|
+
generateEntityProperties(entities, aliases, generatedServices, options))
|
|
1113
1210
|
};
|
|
1114
1211
|
};
|
|
1115
1212
|
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
const extractSchemas = (doc) => {
|
|
1119
|
-
const schemas = new Map();
|
|
1213
|
+
const parseReferencedEntityType = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
|
|
1214
|
+
const extractServiceAliases = (doc, schemas) => {
|
|
1120
1215
|
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
1216
|
for (const [path, methods] of Object.entries(doc.paths)) {
|
|
1131
1217
|
const parsed = parseEndpointPath(path);
|
|
1132
|
-
if (!parsed || schemas.has(parsed.
|
|
1218
|
+
if (!parsed || !methods || parsed.type !== WeclappEndpointType.ROOT || schemas.has(parsed.service)) {
|
|
1133
1219
|
continue;
|
|
1134
1220
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1221
|
+
const body = methods[OpenAPIV3.HttpMethods.GET]?.responses['200'];
|
|
1222
|
+
if (isResponseObject(body) && body.content?.['application/json']) {
|
|
1223
|
+
const responseSchema = body.content['application/json'].schema;
|
|
1224
|
+
if (!responseSchema || isReferenceObject(responseSchema)) {
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
const resultSchema = responseSchema.properties?.result;
|
|
1228
|
+
if (!resultSchema) {
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (isReferenceObject(resultSchema)) {
|
|
1232
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultSchema));
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
if (isArraySchemaObject(resultSchema)) {
|
|
1236
|
+
const resultItemSchema = resultSchema.items;
|
|
1237
|
+
if (isReferenceObject(resultItemSchema)) {
|
|
1238
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultItemSchema));
|
|
1152
1239
|
}
|
|
1153
1240
|
}
|
|
1154
1241
|
}
|
|
1155
1242
|
}
|
|
1243
|
+
return aliases;
|
|
1244
|
+
};
|
|
1245
|
+
const extractSchemas = (doc) => {
|
|
1246
|
+
const schemas = new Map();
|
|
1247
|
+
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
|
|
1248
|
+
if (!isReferenceObject(schema)) {
|
|
1249
|
+
schemas.set(name, schema);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
const aliases = extractServiceAliases(doc, schemas);
|
|
1156
1253
|
return { schemas, aliases };
|
|
1157
1254
|
};
|
|
1158
1255
|
|
|
@@ -1160,14 +1257,10 @@ const generate = (doc, options) => {
|
|
|
1160
1257
|
const { schemas, aliases } = extractSchemas(doc);
|
|
1161
1258
|
const enums = generateEnums(schemas);
|
|
1162
1259
|
const entities = generateEntities(schemas, enums);
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
options,
|
|
1168
|
-
entities,
|
|
1169
|
-
aliases
|
|
1170
|
-
}).source));
|
|
1260
|
+
const entityFilterProps = generateEntityFilterProps(entities, enums);
|
|
1261
|
+
const services = generateServices(doc.paths, entities, aliases, options);
|
|
1262
|
+
const maps = generateMaps(enums, entities, services, aliases, options);
|
|
1263
|
+
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
1264
|
};
|
|
1172
1265
|
|
|
1173
1266
|
const hash = (content, algorithm = 'sha256') => {
|
|
@@ -1183,12 +1276,13 @@ const hash = (content, algorithm = 'sha256') => {
|
|
|
1183
1276
|
|
|
1184
1277
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
1185
1278
|
const cli = async () => {
|
|
1186
|
-
const
|
|
1279
|
+
const version = pkg.version;
|
|
1187
1280
|
const { argv } = yargs(hideBin(process.argv))
|
|
1188
1281
|
.scriptName('build-weclapp-sdk')
|
|
1189
1282
|
.usage('Usage: $0 <source> [flags]')
|
|
1190
1283
|
.version(version)
|
|
1191
1284
|
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1285
|
+
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1192
1286
|
.example('$0 xxx.weclapp.com --key ...', 'Generate the SDK based on the openapi file from the given weclapp instance')
|
|
1193
1287
|
.help('h')
|
|
1194
1288
|
.alias('v', 'version')
|
|
@@ -1228,36 +1322,47 @@ const cli = async () => {
|
|
|
1228
1322
|
type: 'string',
|
|
1229
1323
|
choices: ['browser', 'browser.rx', 'node', 'node.rx']
|
|
1230
1324
|
})
|
|
1231
|
-
.option('
|
|
1232
|
-
|
|
1233
|
-
describe: 'Include deprecated functions and services',
|
|
1325
|
+
.option('use-query-language', {
|
|
1326
|
+
describe: 'Generate the new where property for some and count queries',
|
|
1234
1327
|
type: 'boolean'
|
|
1328
|
+
})
|
|
1329
|
+
.option('apiVersion', {
|
|
1330
|
+
describe: 'Specify the api version (only needed when not using a local file)',
|
|
1331
|
+
type: 'string'
|
|
1235
1332
|
})
|
|
1236
1333
|
.epilog(`Copyright ${new Date().getFullYear()} weclapp GmbH`);
|
|
1237
1334
|
if (argv.fromEnv) {
|
|
1238
1335
|
config();
|
|
1239
1336
|
}
|
|
1240
1337
|
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;
|
|
1338
|
+
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, apiVersion, _: [src = WECLAPP_BACKEND_URL] } = argv;
|
|
1242
1339
|
const options = {
|
|
1243
1340
|
deprecated,
|
|
1244
1341
|
generateUnique: argv.generateUnique ?? false,
|
|
1245
|
-
target: argv.target ?? Target.BROWSER_PROMISES
|
|
1342
|
+
target: argv.target ?? Target.BROWSER_PROMISES,
|
|
1343
|
+
useQueryLanguage: argv.useQueryLanguage ?? false
|
|
1246
1344
|
};
|
|
1247
|
-
if (typeof src === 'number') {
|
|
1248
|
-
return Promise.reject('Expected string as command');
|
|
1345
|
+
if (!src || typeof src === 'number') {
|
|
1346
|
+
return Promise.reject(new Error('Expected string as command'));
|
|
1249
1347
|
}
|
|
1250
1348
|
if (!Object.values(Target).includes(options.target)) {
|
|
1251
1349
|
logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(', ')}`);
|
|
1252
|
-
return Promise.reject();
|
|
1350
|
+
return Promise.reject(new Error());
|
|
1253
1351
|
}
|
|
1254
1352
|
if (await stat(src).catch(() => false)) {
|
|
1255
|
-
logger.infoLn(`Source is a file
|
|
1353
|
+
logger.infoLn(`Source is a file`);
|
|
1256
1354
|
const content = JSON.parse(await readFile(src, 'utf-8'));
|
|
1257
1355
|
return { cache, content, options };
|
|
1258
1356
|
}
|
|
1357
|
+
logger.infoLn(`Source is a URL`);
|
|
1358
|
+
if (!key) {
|
|
1359
|
+
return Promise.reject(new Error('API key is missing'));
|
|
1360
|
+
}
|
|
1361
|
+
if (!apiVersion) {
|
|
1362
|
+
return Promise.reject(new Error('API version is missing'));
|
|
1363
|
+
}
|
|
1259
1364
|
const url = new URL(src.startsWith('http') ? src : `https://${src}`);
|
|
1260
|
-
url.pathname =
|
|
1365
|
+
url.pathname = `/webapp/api/${apiVersion}/meta/openapi.json`;
|
|
1261
1366
|
if (query?.length) {
|
|
1262
1367
|
for (const param of query.split(',')) {
|
|
1263
1368
|
const [name, value] = param.split('=');
|
|
@@ -1265,11 +1370,11 @@ const cli = async () => {
|
|
|
1265
1370
|
}
|
|
1266
1371
|
}
|
|
1267
1372
|
const content = await fetch(url.toString(), {
|
|
1268
|
-
headers: {
|
|
1269
|
-
}).then(res => res.ok ? res.json() : undefined);
|
|
1373
|
+
headers: { Accept: 'application/json', AuthenticationToken: key }
|
|
1374
|
+
}).then((res) => (res.ok ? res.json() : undefined));
|
|
1270
1375
|
if (!content) {
|
|
1271
1376
|
logger.errorLn(`Couldn't fetch file ${url.toString()} `);
|
|
1272
|
-
return Promise.reject();
|
|
1377
|
+
return Promise.reject(new Error());
|
|
1273
1378
|
}
|
|
1274
1379
|
else {
|
|
1275
1380
|
logger.infoLn(`Use remote file: ${url.toString()}`);
|
|
@@ -1277,50 +1382,59 @@ const cli = async () => {
|
|
|
1277
1382
|
return { cache, content, options };
|
|
1278
1383
|
};
|
|
1279
1384
|
|
|
1280
|
-
const
|
|
1281
|
-
const
|
|
1385
|
+
const workingDir = resolve(currentDirname(), './sdk');
|
|
1386
|
+
const cacheDir = resolve(currentDirname(), './.cache');
|
|
1282
1387
|
void (async () => {
|
|
1283
1388
|
const start = process.hrtime.bigint();
|
|
1284
|
-
const { default: { version } } = await import('../package.json', { assert: { type: 'json' } });
|
|
1285
1389
|
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);
|
|
1390
|
+
const workingDirPath = async (...paths) => {
|
|
1391
|
+
const fullPath = resolve(workingDir, ...paths);
|
|
1392
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
1293
1393
|
return fullPath;
|
|
1294
1394
|
};
|
|
1395
|
+
// Resolve cache dir and key
|
|
1396
|
+
const cacheKey = hash([pkg.version, JSON.stringify(doc), JSON.stringify(options)]).slice(-8);
|
|
1397
|
+
const cachedSdkDir = resolve(cacheDir, cacheKey);
|
|
1398
|
+
// Remove old SDK
|
|
1399
|
+
await rm(workingDir, { recursive: true, force: true });
|
|
1295
1400
|
if (useCache) {
|
|
1296
1401
|
logger.infoLn(`Cache ID: ${cacheKey}`);
|
|
1297
1402
|
}
|
|
1298
|
-
if (useCache && await stat(
|
|
1299
|
-
|
|
1403
|
+
if (useCache && (await stat(cachedSdkDir).catch(() => false))) {
|
|
1404
|
+
// Copy cached SDK to working dir
|
|
1405
|
+
logger.successLn(`Cache match! (${cachedSdkDir})`);
|
|
1406
|
+
await cp(cachedSdkDir, workingDir, { recursive: true });
|
|
1300
1407
|
}
|
|
1301
1408
|
else {
|
|
1302
|
-
//
|
|
1303
|
-
await writeFile(await
|
|
1409
|
+
// Write openapi.json file
|
|
1410
|
+
await writeFile(await workingDirPath('openapi.json'), JSON.stringify(doc, null, 2));
|
|
1304
1411
|
logger.infoLn(`Generate sdk (target: ${options.target})`);
|
|
1305
|
-
// Generate
|
|
1412
|
+
// Generate and write SDK (index.ts)
|
|
1306
1413
|
const sdk = generate(doc, options);
|
|
1307
|
-
await writeFile(await
|
|
1308
|
-
// Bundle
|
|
1414
|
+
await writeFile(await workingDirPath('src', 'index.ts'), sdk.trim() + '\n');
|
|
1415
|
+
// Bundle and write SDK
|
|
1309
1416
|
logger.infoLn('Bundle... (this may take some time)');
|
|
1310
|
-
await bundle(
|
|
1311
|
-
// Remove
|
|
1312
|
-
await
|
|
1417
|
+
await bundle(workingDir, options.target);
|
|
1418
|
+
// Remove index.ts (only bundle is required)
|
|
1419
|
+
await rm(await workingDirPath('src'), { recursive: true, force: true });
|
|
1420
|
+
if (useCache) {
|
|
1421
|
+
// Copy SDK to cache
|
|
1422
|
+
logger.successLn(`Caching SDK: (${cachedSdkDir})`);
|
|
1423
|
+
await mkdir(cachedSdkDir, { recursive: true });
|
|
1424
|
+
await cp(workingDir, cachedSdkDir, { recursive: true });
|
|
1425
|
+
}
|
|
1313
1426
|
}
|
|
1314
|
-
// Copy bundled SDK
|
|
1315
|
-
await cp(cacheDir, workingDirectory, { recursive: true });
|
|
1316
1427
|
// Print job summary
|
|
1317
1428
|
const duration = (process.hrtime.bigint() - start) / 1000000n;
|
|
1318
1429
|
logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
|
|
1319
1430
|
logger.printSummary();
|
|
1320
|
-
})()
|
|
1431
|
+
})()
|
|
1432
|
+
.catch((error) => {
|
|
1321
1433
|
logger.errorLn(`Fatal error:`);
|
|
1322
1434
|
/* eslint-disable no-console */
|
|
1323
1435
|
console.error(error);
|
|
1324
|
-
})
|
|
1325
|
-
|
|
1436
|
+
})
|
|
1437
|
+
.finally(() => {
|
|
1438
|
+
if (logger.errors)
|
|
1439
|
+
process.exit(1);
|
|
1326
1440
|
});
|