@weclapp/sdk 1.8.0-dev.16

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/dist/cli.js ADDED
@@ -0,0 +1,1397 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { resolve, dirname } from 'path';
3
+ import { rmdir, stat, readFile, writeFile, rm, cp, mkdir } from 'fs/promises';
4
+ import { rollup } from 'rollup';
5
+ import terser from '@rollup/plugin-terser';
6
+ import ts from 'rollup-plugin-ts';
7
+ import indentString from 'indent-string';
8
+ import { snakeCase, pascalCase, camelCase } from 'change-case';
9
+ import chalk from 'chalk';
10
+ import { OpenAPIV3 } from 'openapi-types';
11
+ import { createHash } from 'crypto';
12
+ import { config } from 'dotenv';
13
+ import yargs from 'yargs';
14
+ import { hideBin } from 'yargs/helpers';
15
+ import prettyMs from 'pretty-ms';
16
+
17
+ var Target;
18
+ (function (Target) {
19
+ Target["BROWSER_PROMISES"] = "browser";
20
+ Target["BROWSER_RX"] = "browser.rx";
21
+ Target["NODE_PROMISES"] = "node";
22
+ Target["NODE_RX"] = "node.rx";
23
+ })(Target || (Target = {}));
24
+ const isNodeTarget = (target) => {
25
+ return target === Target.NODE_PROMISES || target === Target.NODE_RX;
26
+ };
27
+ const isRXTarget = (target) => {
28
+ return target === Target.BROWSER_RX || target === Target.NODE_RX;
29
+ };
30
+ const resolveResponseType = (target) => {
31
+ return isRXTarget(target) ? 'Observable' : 'Promise';
32
+ };
33
+ const resolveBinaryType = (target) => {
34
+ return isNodeTarget(target) ? 'Buffer' : 'Blob';
35
+ };
36
+
37
+ const currentDirname = () => {
38
+ // Go one level up as the CLI is inside a folder
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, '*']));
44
+ const generateOutput = (config) => ({
45
+ sourcemap: true,
46
+ banner: `/* weclapp sdk */`,
47
+ ...config
48
+ });
49
+ const bundle = async (workingDirectory, target) => {
50
+ const dist = (...paths) => resolve(workingDirectory, 'dist', ...paths);
51
+ const src = (...paths) => resolve(workingDirectory, 'src', ...paths);
52
+ const generateNodeOutput = () => [
53
+ generateOutput({
54
+ file: dist('index.cjs'),
55
+ format: 'cjs',
56
+ globals: resolveGlobals('node-fetch', 'url')
57
+ }),
58
+ generateOutput({
59
+ file: dist('index.js'),
60
+ format: 'es',
61
+ globals: resolveGlobals('node-fetch', 'url')
62
+ })
63
+ ];
64
+ // Remove build dir
65
+ await rmdir(dist()).catch(() => void 0);
66
+ const bundles = {
67
+ [Target.BROWSER_PROMISES]: () => ({
68
+ input: src('browser.ts'),
69
+ output: [
70
+ generateOutput({
71
+ file: dist('index.cjs'),
72
+ name: 'Weclapp',
73
+ format: 'umd'
74
+ }),
75
+ generateOutput({
76
+ file: dist('index.js'),
77
+ format: 'es'
78
+ })
79
+ ],
80
+ plugins: [ts({ tsconfig }), terser()]
81
+ }),
82
+ [Target.BROWSER_RX]: () => ({
83
+ external: ['rxjs'],
84
+ input: src('browser.rx.ts'),
85
+ plugins: [ts({ tsconfig }), terser()],
86
+ output: [
87
+ generateOutput({
88
+ file: dist('index.cjs'),
89
+ name: 'Weclapp',
90
+ format: 'umd',
91
+ globals: resolveGlobals('rxjs')
92
+ }),
93
+ generateOutput({
94
+ file: dist('index.js'),
95
+ format: 'es',
96
+ globals: resolveGlobals('rxjs')
97
+ })
98
+ ]
99
+ }),
100
+ [Target.NODE_PROMISES]: () => ({
101
+ input: src('node.ts'),
102
+ output: generateNodeOutput(),
103
+ external: ['node-fetch', 'url'],
104
+ plugins: [ts({ tsconfig })]
105
+ }),
106
+ [Target.NODE_RX]: () => ({
107
+ input: src('node.rx.ts'),
108
+ output: generateNodeOutput(),
109
+ external: ['node-fetch', 'url', 'rxjs'],
110
+ plugins: [ts({ tsconfig })]
111
+ })
112
+ };
113
+ const config = bundles[target]();
114
+ const bundle = await rollup(config);
115
+ if (Array.isArray(config.output)) {
116
+ await Promise.all(config.output.map(bundle.write));
117
+ }
118
+ else if (config.output) {
119
+ await bundle.write(config.output);
120
+ }
121
+ await bundle.close();
122
+ };
123
+
124
+ const generateString = (str) => `'${str}'`;
125
+ const generateStrings = (str) => str.map(generateString);
126
+
127
+ const generateImport = (opt) => {
128
+ const imports = [opt.default, opt.imports?.length ? `{${opt.imports.join(', ')}}` : ''];
129
+ return `import ${imports.filter(Boolean).join(', ')} from ${generateString(opt.src)};`;
130
+ };
131
+
132
+ /**
133
+ * Indents each line of the given string
134
+ * @param s String to indent
135
+ * @param level Indentation level
136
+ */
137
+ const indent = (s, level = 1) => {
138
+ return indentString(s, 4 * level);
139
+ };
140
+
141
+ const generateStatements = (...statements) => statements
142
+ .map(v => v.trim())
143
+ .filter(v => v.length)
144
+ .join('\n\n');
145
+ const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
146
+
147
+ var root = "export type RequestPayloadMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | '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\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.\n host?: string;\n\n // If you want to use https, defaults to true.\n secure?: boolean;\n\n // Optional request/response interceptors.\n interceptors?: {\n\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?: (request: Request, payload: RequestPayload) => 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\nlet globalConfig: ServiceConfig | undefined;\nexport const getGlobalConfig = (): ServiceConfig | undefined => globalConfig;\nexport const setGlobalConfig = (cfg?: ServiceConfig) => globalConfig = cfg;\n\nexport const raw = async (\n cfg: ServiceConfig | undefined = globalConfig,\n endpoint: string,\n payload: RequestPayload = {}\n): Promise<any> => {\n if (!cfg) {\n throw new Error(`ServiceConfig missing.`);\n }\n\n cfg = {\n ...globalConfig, ...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 const protocol = (cfg.secure ?? true) ? 'https' : 'http';\n\n const interceptRequest = cfg.interceptors?.request ?? (v => v);\n const interceptResponse = cfg.interceptors?.response ?? (v => v);\n\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 domain');\n }\n\n const request = new Request(`${protocol}://${host}/webapp/api/v1${endpoint}?${params}`, {\n ...(payload.body && {\n body: isBinaryData\n ? payload.body\n : JSON.stringify(payload.body, (key, value) => value === undefined ? null : value)\n }),\n ...(!cfg.key && {credentials: 'same-origin'}),\n method: payload.method ?? 'get',\n headers: {\n 'Accept': 'application/json',\n ...(cfg.key && {'AuthenticationToken': cfg.key}),\n ...(!isBinaryData && {'Content-Type': 'application/json'})\n }\n });\n\n let res = await interceptRequest(request, payload) ?? request;\n if (!(res instanceof Response)) {\n res = await fetch(res);\n }\n\n res = await interceptResponse(res) ?? res;\n const data = (!payload.forceBlob || !res.ok) && res.headers?.get('content-type')?.includes('application/json') ?\n await res.json() : await res.blob();\n\n // Check if response was successful\n if (!res.ok) {\n return Promise.reject(data);\n }\n\n return payload.unwrap ? data.result : data;\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & {params?: Record<any, any>}\n) => wrapResponse(() => raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\n }\n}));\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any> & {params?: Record<any, any>}\n) => wrapResponse(() => raw(cfg, endpoint, {\n query: {\n serializeNulls: query?.serializeNulls,\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 }).then(data => ({entities: data.result, references: data.referencedEntities ?? {}}))\n);\n\nconst _remove = (\n cfg: ServiceConfig | undefined,\n endpoint: string\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'DELETE'\n}).then(() => undefined));\n\nconst _create = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n data: any\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'POST',\n body: data\n}));\n\nconst _update = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n data: any,\n {ignoreMissingProperties = true}: UpdateQuery = {}\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'PUT',\n body: data,\n query: {ignoreMissingProperties}\n}));\n\nconst _unique = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: UniqueQuery\n) => wrapResponse(() => raw(cfg, endpoint, {query}));\n\nconst _generic = (\n cfg: ServiceConfig | undefined,\n method: RequestPayloadMethod,\n endpoint: string,\n payload?: GenericQuery<any, any>,\n forceBlob?: boolean\n) => wrapResponse(() => raw(cfg, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n}));\n";
148
+
149
+ var types$1 = "export type DeepPartial<T> = T extends object ? {\n [P in keyof T]?: DeepPartial<T[P]>;\n} : T;\n\n// Filter properties\nexport type EqualityOperators = 'EQ' | 'NE';\nexport type ComparisonOperators = 'LT' | 'GT' | 'LE' | 'GE' | 'LIKE' | 'ILIKE' | 'NOT_LIKE' | 'NOT_ILIKE';\nexport type ArrayOperators = 'IN' | 'NOT_IN';\nexport type Operator = EqualityOperators | ComparisonOperators | ArrayOperators;\n\nexport type MapOperators<T> = { [K in EqualityOperators]?: T | null; } &\n { [K in ComparisonOperators]?: T; } &\n { [K in ArrayOperators]?: T[]; };\n\nexport type QueryFilter<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ?\n U extends Record<any, any> ? QueryFilter<U> : MapOperators<U> :\n T[P] extends Record<any, any> | undefined ? QueryFilter<T[P]> : MapOperators<T[P]>;\n};\n\nexport type Sort<T> = {\n [K in keyof T]?: {\n [V in keyof T]?:\n V extends K ?\n T[V] extends Array<infer U> | undefined ?\n U extends object ?\n Sort<U> : never :\n T[V] extends object | undefined ?\n Sort<T[V]> : 'asc' | 'desc' : never;\n };\n}[keyof T];\n\n// Select properties\nexport type CustomAttributeFilter = {\n [K in number]: string | number | boolean |\n {id: string;} |\n {entityName: string; entityId: string;};\n}\n\nexport type QuerySelect<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ? (QuerySelect<U> | boolean) :\n T[P] extends Record<any, any> | undefined ? (QuerySelect<T[P]> | boolean) : boolean;\n}\n\nexport type Select<T, Q extends (QuerySelect<T> | undefined)> = 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]:\n\n // Property\n Q[P] extends true ? T[P] :\n\n // Array\n T[P] extends Array<infer U> ? Select<U, Q[P] & QuerySelect<any>>[] :\n\n // Object\n T[P] extends Record<any, any> ? Select<T[P], Q[P] & QuerySelect<any>> : never\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\n// Endpoint configurations\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n};\n\nexport type Pagination = {\n page: number;\n pageSize: number;\n};\n\nexport type SomeQuery<\n E, // Entity\n F, // Entity filter\n I extends (QuerySelect<any> | undefined), // Select for referenced entities\n S extends (QuerySelect<any> | undefined) // Select for entity properties\n> = {\n serializeNulls?: boolean;\n include?: I;\n filter?: QueryFilter<F> & CustomAttributeFilter;\n select?: S;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nexport type UniqueQuery = {\n serializeNulls?: boolean;\n}\n\nexport type SomeQueryReturn<\n E, // Entity\n R, // Map of referenced-entity names to the type\n M, // Map of referenced-entity-id names to their entity name\n I extends (QuerySelect<any> | undefined), // Select for referenced entities\n S extends (QuerySelect<any> | undefined) // Select for entity properties\n> = {\n entities: (S extends QuerySelect<E> ? Select<E, S> : E)[];\n references: I extends QuerySelect<R> ? Partial<MapKeys<Select<R, I>, M & Record<any, any>>> : {};\n};\n\nexport type GenericQuery<P, B> = {\n params?: P;\n body?: B;\n};\n\nexport type UpdateQuery = {\n ignoreMissingProperties?: boolean;\n}\n\n// Entity meta types\nexport type WEntityPropertyMeta = (\n { type: 'string'; format?: 'decimal' | 'html'; entity?: WEntity; service?: WService; } |\n { type: 'integer'; format: 'int32' | 'int64' | 'duration' | 'date' | 'timestamp'; } |\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);\n\n// Utils\nconst equality: string[] = ['EQ', 'NE'];\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\nconst array: string[] = ['IN', 'NOT_IN'];\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};\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)) {\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 if (array.includes(filter)) {\n entries.push([\n `${prop}-${filterMap[filter as Operator]}`,\n `[${value.map((v: string | number) => typeof v === 'string' ? `\"${v}\"` : v)}]`\n ]);\n } else {\n entries.push(\n ...flatten(propValue as QueryFilter<any>)\n .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][] = [], 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(\n ...flatten(obj[i])\n .map(v => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]\n );\n }\n\n return Object.fromEntries(entries);\n};\n\nconst 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
+
151
+ const resolveImports = (target) => {
152
+ const imports = [];
153
+ if (isRXTarget(target)) {
154
+ imports.push(generateImport({ src: 'rxjs', imports: ['defer', 'Observable'] }));
155
+ }
156
+ return imports.join('\n');
157
+ };
158
+ const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
159
+ const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
160
+ const generateBase = (target) => {
161
+ return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), types$1, root);
162
+ };
163
+
164
+ const transformKey = (s) => snakeCase(s).toUpperCase();
165
+ const generateEnum = (name, values) => {
166
+ const props = indent(values.map(v => `${transformKey(v)} = ${generateString(v)}`).join(',\n'));
167
+ return `export enum ${name} {\n${props}\n}`;
168
+ };
169
+
170
+ // We can't use the pascalCase utility as it converts "cDBReminderType" to "CDbReminderType" which is incorrect.
171
+ const loosePascalCase = (str) => str[0].toUpperCase() + str.slice(1);
172
+
173
+ const isObject = (v) => {
174
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
175
+ };
176
+ const isParameterObject = (v) => {
177
+ return isObject(v) && typeof v.name === 'string' && typeof v.in === 'string';
178
+ };
179
+ const isReferenceObject = (v) => {
180
+ return isObject(v) && typeof v.$ref === 'string';
181
+ };
182
+ const isObjectSchemaObject = (v) => {
183
+ return isObject(v) && v.type === 'object' && isObject(v.properties);
184
+ };
185
+ const isEnumSchemaObject = (v) => {
186
+ return isObject(v) && v.type === 'string' && Array.isArray(v.enum);
187
+ };
188
+ const isArraySchemaObject = (v) => {
189
+ return isObject(v) && v.type === 'array' && typeof v.items === 'object';
190
+ };
191
+ const isResponseObject = (v) => {
192
+ return isObject(v) && typeof v.description === 'string';
193
+ };
194
+ const isNonArraySchemaObject = (v) => {
195
+ return isObject(v) && ['string', 'undefined'].includes(typeof v.type);
196
+ };
197
+ const isRelatedEntitySchema = (v) => {
198
+ return isObject(v) && isNonArraySchemaObject(v) && 'x-weclapp' in v && isObject(v['x-weclapp']);
199
+ };
200
+
201
+ const generateEnums = (schemas) => {
202
+ const enums = new Map();
203
+ for (const [propName, schema] of schemas) {
204
+ if (isEnumSchemaObject(schema)) {
205
+ const name = loosePascalCase(propName);
206
+ if (!enums.has(name)) {
207
+ enums.set(name, {
208
+ properties: schema.enum,
209
+ source: generateEnum(name, schema.enum)
210
+ });
211
+ }
212
+ }
213
+ }
214
+ return enums;
215
+ };
216
+
217
+ const concat = (strings, separator = ', ', maxLength = 80) => {
218
+ const joined = strings.join(separator);
219
+ if (joined.length > maxLength) {
220
+ const length = strings.length - 1;
221
+ return `\n${indent(strings
222
+ .map((value, index) => index === length ? value : `${(value + separator).trim()}\n`)
223
+ .join(''))}\n`;
224
+ }
225
+ else {
226
+ return joined;
227
+ }
228
+ };
229
+
230
+ /* eslint-disable no-use-before-define */
231
+ const createReferenceType = (value) => ({
232
+ type: 'reference',
233
+ toString: () => loosePascalCase(value)
234
+ });
235
+ const createRawType = (value) => ({
236
+ type: 'raw',
237
+ toString: () => value
238
+ });
239
+ const createArrayType = (value) => ({
240
+ type: 'array',
241
+ toString: () => `${value.toString()}[]`
242
+ });
243
+ const createTupleType = (value) => ({
244
+ type: 'tuple',
245
+ toString: () => concat([...new Set(value.map(v => typeof v === 'string' ? `'${v}'` : v.toString()))], ' | ')
246
+ });
247
+ const createObjectType = (value, required = []) => ({
248
+ type: 'object',
249
+ isFullyOptional: () => {
250
+ return !required.length && Object.values(value)
251
+ .filter(v => v?.type === 'object')
252
+ .every(v => v.isFullyOptional());
253
+ },
254
+ toString: (propagateOptionalProperties = false) => {
255
+ const properties = Object.entries(value)
256
+ .filter(v => v[1])
257
+ .map(v => {
258
+ const name = v[0];
259
+ const value = v[1];
260
+ const isRequired = required.includes(name) || (value.type === 'object' && !value.isFullyOptional() && propagateOptionalProperties);
261
+ return `${name + (isRequired ? '' : '?')}: ${value.toString()};`;
262
+ });
263
+ return properties.length ? `{\n${indent(properties.join('\n'))}\n}` : '{}';
264
+ }
265
+ });
266
+ const getRefName = (obj) => {
267
+ return obj.$ref.replace(/.*\//, '');
268
+ };
269
+ const convertToTypeScriptType = (schema, property) => {
270
+ if (isReferenceObject(schema)) {
271
+ return createReferenceType(getRefName(schema));
272
+ }
273
+ else {
274
+ switch (schema.type) {
275
+ case 'integer':
276
+ case 'number':
277
+ return createRawType('number');
278
+ case 'string':
279
+ if (schema.enum) {
280
+ return property ? createReferenceType(property) : createTupleType(schema.enum);
281
+ }
282
+ else {
283
+ return schema.format === 'binary' ? createRawType('binary') : createRawType('string');
284
+ }
285
+ case 'boolean':
286
+ return createRawType('boolean');
287
+ case 'object': {
288
+ const { properties = {}, required = [] } = schema;
289
+ return createObjectType(Object.fromEntries(Object.entries(properties)
290
+ .map(v => [v[0], convertToTypeScriptType(v[1])])), required);
291
+ }
292
+ case 'array':
293
+ return createArrayType(convertToTypeScriptType(schema.items, property));
294
+ default:
295
+ return createRawType('unknown');
296
+ }
297
+ }
298
+ };
299
+
300
+ const setEntityEnumProperty = (enums, prop, meta) => {
301
+ const referenceName = getRefName(prop);
302
+ const enumName = loosePascalCase(referenceName);
303
+ if (enums.has(enumName)) {
304
+ meta.enum = enumName;
305
+ }
306
+ else {
307
+ meta.entity = referenceName;
308
+ }
309
+ };
310
+ const extractPropertyMetaData = (enums, meta, prop) => {
311
+ const result = { service: meta.service, entity: meta.entity };
312
+ if (isReferenceObject(prop)) {
313
+ setEntityEnumProperty(enums, prop, result);
314
+ result.type = 'reference';
315
+ return result;
316
+ }
317
+ result.format = prop.format;
318
+ result.type = prop.type;
319
+ if (isArraySchemaObject(prop)) {
320
+ if (isReferenceObject(prop.items)) {
321
+ setEntityEnumProperty(enums, prop.items, result);
322
+ result.format = 'reference';
323
+ }
324
+ else {
325
+ result.format = 'string';
326
+ }
327
+ }
328
+ return result;
329
+ };
330
+
331
+ const generateInlineComment = (comment) => `/** ${comment} */`;
332
+ const generateBlockComment = (comment, body) => `/**\n${comment.trim().replace(/^ */gm, ' * ')}\n */${body ? `\n${body}` : ''}`;
333
+
334
+ const generateType = (name, value) => {
335
+ return `export type ${name} = ${value.trim()};`;
336
+ };
337
+
338
+ const arrayify = (v) => Array.isArray(v) ? v : [v];
339
+
340
+ const generateInterfaceProperties = (entries) => {
341
+ const properties = entries
342
+ .filter(v => v.type !== undefined)
343
+ .filter((value, index, array) => array.findIndex(v => v.name === value.name) === index)
344
+ .map(({ name, type, required, readonly, comment }) => {
345
+ const cmd = comment ? `${generateInlineComment(comment)}\n` : '';
346
+ const req = required ? '' : '?';
347
+ const rol = readonly ? 'readonly ' : '';
348
+ return `${cmd + rol + name + req}: ${type};`;
349
+ })
350
+ .join('\n');
351
+ return properties.length ? `{\n${indent(properties)}\n}` : `{}`;
352
+ };
353
+ const generateInterfaceFromObject = (name, obj, propagateOptionalProperties) => `export interface ${name} ${obj.toString(propagateOptionalProperties)}`;
354
+ const generateInterface = (name, entries, extend) => {
355
+ const signature = `${name} ${extend ? `extends ${arrayify(extend).join(', ')}` : ''}`.trim();
356
+ const body = generateInterfaceProperties(entries);
357
+ return `export interface ${signature} ${body}`;
358
+ };
359
+ const generateInterfaceType = (name, entries, extend) => {
360
+ const body = generateInterfaceProperties(entries);
361
+ const bases = extend ? arrayify(extend).join(' & ') : undefined;
362
+ return generateType(name, `${bases ? `${bases} & ` : ''}${body}`);
363
+ };
364
+
365
+ const generateEntities = (schemas, enums) => {
366
+ const entities = new Map();
367
+ for (const [schemaName, schema] of schemas) {
368
+ // Enums are generated separately
369
+ if (isEnumSchemaObject(schema)) {
370
+ continue;
371
+ }
372
+ const entity = pascalCase(schemaName);
373
+ // Entity and filter
374
+ const entityInterface = [];
375
+ const filterInterface = [];
376
+ // Referenced entities and property-to-referenced-entity mapping
377
+ const referenceInterface = [];
378
+ const referenceMappingsInterface = [];
379
+ const properties = new Map();
380
+ // The parent entity
381
+ let extend = undefined;
382
+ const processProperties = (props = {}) => {
383
+ for (const [name, property] of Object.entries(props)) {
384
+ const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
385
+ if (meta.entity) {
386
+ const type = `${pascalCase(meta.entity)}[]`;
387
+ referenceInterface.push({ name, type, required: true });
388
+ filterInterface.push({ name: meta.entity, type, required: true });
389
+ }
390
+ if (meta.service) {
391
+ referenceMappingsInterface.push({ name, type: generateString(meta.service), required: true });
392
+ }
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
+ }
405
+ };
406
+ if (schema.allOf?.length) {
407
+ for (const item of schema.allOf) {
408
+ if (isReferenceObject(item)) {
409
+ extend = convertToTypeScriptType(item).toString();
410
+ }
411
+ else if (isObjectSchemaObject(item)) {
412
+ processProperties(item.properties);
413
+ }
414
+ }
415
+ }
416
+ 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
+ entities.set(schemaName, {
419
+ extends: extend ? camelCase(extend) : extend,
420
+ properties,
421
+ source
422
+ });
423
+ }
424
+ return entities;
425
+ };
426
+
427
+ /**
428
+ * Pluralizes a word, most of the time correct.
429
+ * @param s String to pluralize.
430
+ */
431
+ const pluralize = (s) => {
432
+ return s.endsWith('s') ? s :
433
+ s.endsWith('y') ? `${s.slice(0, -1)}ies` :
434
+ `${s}s`;
435
+ };
436
+
437
+ /* eslint-disable no-console */
438
+ const logger = new class {
439
+ active = true;
440
+ warnings = 0;
441
+ errors = 0;
442
+ write(str = '') {
443
+ process.stdout.write(str);
444
+ }
445
+ blankLn(str = '') {
446
+ this.blank(`${str}\n`);
447
+ }
448
+ warnLn(str) {
449
+ this.warn(`${str}\n`);
450
+ }
451
+ errorLn(str) {
452
+ this.error(`${str}\n`);
453
+ }
454
+ successLn(str) {
455
+ this.success(`${str}\n`);
456
+ }
457
+ infoLn(str) {
458
+ this.info(`${str}\n`);
459
+ }
460
+ debugLn(str) {
461
+ this.debug(`${str}\n`);
462
+ }
463
+ blank(str) {
464
+ this.write(str);
465
+ }
466
+ warn(str) {
467
+ this.write(`${chalk.yellowBright('[!]')} ${str}`);
468
+ this.warnings++;
469
+ }
470
+ error(str) {
471
+ this.write(`${chalk.redBright('[X]')} ${str}`);
472
+ this.errors++;
473
+ }
474
+ success(str) {
475
+ this.write(`${chalk.greenBright('[✓]')} ${str}`);
476
+ }
477
+ info(str) {
478
+ this.write(`${chalk.blueBright('[i]')} ${str}`);
479
+ }
480
+ debug(str) {
481
+ this.write(`[-] ${str}`);
482
+ }
483
+ printSummary() {
484
+ const format = (v, name, fail, ok) => {
485
+ const color = v ? fail : ok;
486
+ return v === 0 ? `${color('zero')} ${pluralize(name)}` :
487
+ v === 1 ? `${color('one')} ${name}` : `${color(v)} ${pluralize(name)}`;
488
+ };
489
+ const warnings = format(this.warnings, 'warning', chalk.yellowBright, chalk.greenBright);
490
+ const errors = format(this.errors, 'error', chalk.redBright, chalk.greenBright);
491
+ const info = `Finished with ${warnings} and ${errors}.`;
492
+ this[this.errors ? 'errorLn' : this.warnings ? 'warnLn' : 'successLn'](info);
493
+ }
494
+ };
495
+
496
+ /**
497
+ * ROOT => /article
498
+ * COUNT => /article/count
499
+ * ENTITY => /article/{id}
500
+ * SPECIAL_ROOT => /article/generateImage
501
+ * SPECIAL_ENTITY => /article/id/{id}/generateImag
502
+ */
503
+ var WeclappEndpointType;
504
+ (function (WeclappEndpointType) {
505
+ WeclappEndpointType["ROOT"] = "ROOT";
506
+ WeclappEndpointType["COUNT"] = "COUNT";
507
+ WeclappEndpointType["ENTITY"] = "ENTITY";
508
+ WeclappEndpointType["GENERIC_ROOT"] = "GENERIC_ROOT";
509
+ WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
510
+ })(WeclappEndpointType || (WeclappEndpointType = {}));
511
+ const parseEndpointPath = (path) => {
512
+ const [, entity, ...rest] = path.split('/');
513
+ if (!entity) {
514
+ return undefined;
515
+ }
516
+ if (!rest.length) {
517
+ return { path, entity, type: WeclappEndpointType.ROOT };
518
+ }
519
+ else if (rest[0] === 'count') {
520
+ return { path, entity, type: WeclappEndpointType.COUNT };
521
+ }
522
+ else if (rest[0] === 'id') {
523
+ return rest.length === 2 ?
524
+ { path, entity, type: WeclappEndpointType.ENTITY } :
525
+ { path, entity, method: rest[2], type: WeclappEndpointType.GENERIC_ENTITY };
526
+ }
527
+ else if (rest.length === 1) {
528
+ return { path, entity, method: rest[1], type: WeclappEndpointType.GENERIC_ROOT };
529
+ }
530
+ return undefined;
531
+ };
532
+
533
+ const generateArrowFunction = ({ name, signature, returns, params }) => {
534
+ return `const ${name}: ${signature} = (${params?.join(', ') ?? ''}) =>\n${indent(returns)};`;
535
+ };
536
+
537
+ const generateArrowFunctionType = ({ type, returns = 'void', generics, params }) => {
538
+ const genericsString = generics?.length ? `<\n${indent(generics.join(',\n'))}\n>` : '';
539
+ const paramsString = params?.length ? `(${params.join(', ')})` : `()`;
540
+ return generateType(type, `${genericsString + paramsString} =>\n${indent(returns)}`);
541
+ };
542
+
543
+ const convertParametersToSchema = (parameters = []) => {
544
+ const properties = [];
545
+ const required = [];
546
+ for (const param of parameters) {
547
+ if (isParameterObject(param) && param.in === 'query') {
548
+ if (param.schema) {
549
+ properties.push([param.name, param.schema]);
550
+ param.required && required.push(param.name);
551
+ }
552
+ }
553
+ }
554
+ return {
555
+ type: 'object', required,
556
+ properties: Object.fromEntries(properties)
557
+ };
558
+ };
559
+
560
+ const functionName$5 = 'count';
561
+ const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
562
+ const service = pascalCase(endpoint.entity);
563
+ const entity = aliases.get(endpoint.entity) ?? service;
564
+ const entityFilter = `${entity}_Filter`;
565
+ const interfaceName = `${service}Service_${pascalCase(functionName$5)}`;
566
+ const entityParameters = `${interfaceName}_Parameters`;
567
+ const parameterSchema = convertParametersToSchema(path.parameters);
568
+ const parameters = createObjectType({
569
+ params: convertToTypeScriptType(parameterSchema)
570
+ });
571
+ const functionSource = generateArrowFunction({
572
+ name: functionName$5,
573
+ signature: interfaceName,
574
+ returns: `_${functionName$5}(cfg, ${generateString(endpoint.path)}, query)`,
575
+ params: ['query']
576
+ });
577
+ const interfaceSource = generateArrowFunctionType({
578
+ type: interfaceName,
579
+ params: [`query${parameters.isFullyOptional() ? '?' : ''}: CountQuery<${entityFilter}> & ${entityParameters}`],
580
+ returns: `${resolveResponseType(target)}<number>`
581
+ });
582
+ return {
583
+ entity,
584
+ name: functionName$5,
585
+ type: { name: interfaceName, source: interfaceSource },
586
+ func: { name: functionName$5, source: functionSource },
587
+ interfaces: [
588
+ {
589
+ name: entityParameters,
590
+ source: generateInterfaceFromObject(entityParameters, parameters, true)
591
+ }
592
+ ]
593
+ };
594
+ };
595
+
596
+ const generateBodyType = (body) => {
597
+ if (isReferenceObject(body)) {
598
+ return convertToTypeScriptType(body);
599
+ }
600
+ const types = [];
601
+ for (const { schema } of Object.values(body?.content ?? {})) {
602
+ if (schema) {
603
+ types.push(convertToTypeScriptType(schema));
604
+ }
605
+ }
606
+ return types.length ? createTupleType(types) : undefined;
607
+ };
608
+
609
+ const generateRequestBodyType = ({ requestBody }) => {
610
+ return generateBodyType(requestBody) ?? createRawType('unknown');
611
+ };
612
+
613
+ const generateResponseBodyType = ({ responses }) => {
614
+ return generateBodyType(Object.entries(responses)
615
+ .filter(v => v[0].startsWith('2'))[0]?.[1]) ?? createRawType('void');
616
+ };
617
+
618
+ const functionName$4 = 'create';
619
+ const generateCreateEndpoint = ({ target, path, endpoint }) => {
620
+ const entity = pascalCase(endpoint.entity);
621
+ const interfaceName = `${entity}Service_${pascalCase(functionName$4)}`;
622
+ const functionSource = generateArrowFunction({
623
+ name: functionName$4,
624
+ signature: interfaceName,
625
+ returns: `_${functionName$4}(cfg, ${generateString(endpoint.path)}, data)`,
626
+ params: ['data']
627
+ });
628
+ const interfaceSource = generateArrowFunctionType({
629
+ type: interfaceName,
630
+ params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`],
631
+ returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
632
+ });
633
+ return {
634
+ entity,
635
+ name: functionName$4,
636
+ type: { name: interfaceName, source: interfaceSource },
637
+ func: { name: functionName$4, source: functionSource }
638
+ };
639
+ };
640
+
641
+ const generateGenericFunctionName = (path, suffix = '', prefix = '') => {
642
+ return camelCase(`${prefix}_` +
643
+ path
644
+ .replace(/.*\//, '')
645
+ .replace(/\W+/, '_')
646
+ .replace(/[_]+/, '_') + `_${suffix}`);
647
+ };
648
+
649
+ const insertPathPlaceholder = (path, record) => {
650
+ return path.replace(/{(\w+)}/g, (_, name) => record[name]);
651
+ };
652
+
653
+ const wrapBody = (type, target) => {
654
+ return type.toString() === 'binary' ?
655
+ createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') :
656
+ type; // node-fetch returns a Blob as well
657
+ };
658
+ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
659
+ const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
660
+ const entity = pascalCase(endpoint.entity);
661
+ const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
662
+ const entityQuery = `${interfaceName}_Query`;
663
+ const hasId = endpoint.path.includes('{id}');
664
+ const params = createObjectType({
665
+ params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
666
+ body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
667
+ });
668
+ const responseBody = generateResponseBodyType(path);
669
+ const forceBlobResponse = String(responseBody.toString() === 'binary');
670
+ const functionSource = generateArrowFunction({
671
+ name: functionName,
672
+ signature: interfaceName,
673
+ params: hasId ? ['id', 'query'] : ['query'],
674
+ returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${forceBlobResponse})`
675
+ });
676
+ const interfaceSource = generateArrowFunctionType({
677
+ type: interfaceName,
678
+ params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
679
+ returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
680
+ });
681
+ return {
682
+ entity,
683
+ name: functionName,
684
+ type: { name: interfaceName, source: interfaceSource },
685
+ func: { name: functionName, source: functionSource },
686
+ interfaces: [
687
+ {
688
+ name: entityQuery,
689
+ source: generateInterfaceFromObject(entityQuery, params, true)
690
+ }
691
+ ]
692
+ };
693
+ };
694
+
695
+ const functionName$3 = 'remove';
696
+ const generateRemoveEndpoint = ({ target, endpoint }) => {
697
+ const entity = pascalCase(endpoint.entity);
698
+ const interfaceName = `${entity}Service_${pascalCase(functionName$3)}`;
699
+ const functionSource = generateArrowFunction({
700
+ name: functionName$3,
701
+ signature: interfaceName,
702
+ returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`)`,
703
+ params: ['id']
704
+ });
705
+ const interfaceSource = generateArrowFunctionType({
706
+ type: interfaceName,
707
+ params: ['id: string'],
708
+ returns: `${resolveResponseType(target)}<void>`
709
+ });
710
+ return {
711
+ entity,
712
+ name: functionName$3,
713
+ type: { name: interfaceName, source: interfaceSource },
714
+ func: { name: functionName$3, source: functionSource }
715
+ };
716
+ };
717
+
718
+ const functionName$2 = 'some';
719
+ const excludedParameters = [
720
+ 'page', 'pageSize', 'sort',
721
+ 'serializeNulls', 'properties', 'includeReferencedEntities'
722
+ ];
723
+ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
724
+ // Required interface names
725
+ const service = pascalCase(endpoint.entity);
726
+ const entity = aliases.get(endpoint.entity) ?? service;
727
+ const interfaceName = `${service}Service_${pascalCase(functionName$2)}`;
728
+ const entityFilter = `${entity}_Filter`;
729
+ const entityMappings = `${entity}_Mappings`;
730
+ const entityReferences = `${entity}_References`;
731
+ const entityParameters = `${service}_Parameters`;
732
+ const parameterSchema = convertParametersToSchema(path.parameters);
733
+ // We already cover page, pageSize and sort
734
+ parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {})
735
+ .filter(v => !excludedParameters.includes(v[0])));
736
+ const parameters = createObjectType({
737
+ params: convertToTypeScriptType(parameterSchema)
738
+ });
739
+ const interfaceSource = generateArrowFunctionType({
740
+ type: interfaceName,
741
+ generics: [
742
+ `S extends (QuerySelect<${entity}> | undefined) = undefined`,
743
+ `I extends (QuerySelect<${entityMappings}> | undefined) = undefined`
744
+ ],
745
+ params: [`query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S> & ${entityParameters}`],
746
+ returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S>>`
747
+ });
748
+ const functionSource = generateArrowFunction({
749
+ name: functionName$2,
750
+ signature: interfaceName,
751
+ returns: `_${functionName$2}(cfg, ${generateString(endpoint.path)}, query)`,
752
+ params: ['query']
753
+ });
754
+ return {
755
+ entity,
756
+ name: functionName$2,
757
+ type: { name: interfaceName, source: interfaceSource },
758
+ func: { name: functionName$2, source: functionSource },
759
+ interfaces: [
760
+ {
761
+ name: entityParameters,
762
+ source: generateInterfaceFromObject(entityParameters, parameters, true)
763
+ }
764
+ ]
765
+ };
766
+ };
767
+
768
+ const functionName$1 = 'unique';
769
+ const generateUniqueEndpoint = ({ target, path, endpoint }) => {
770
+ const entity = pascalCase(endpoint.entity);
771
+ const interfaceName = `${entity}Service_${pascalCase(functionName$1)}`;
772
+ const functionSource = generateArrowFunction({
773
+ name: functionName$1,
774
+ signature: interfaceName,
775
+ params: ['id', 'query'],
776
+ returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
777
+ });
778
+ const interfaceSource = generateArrowFunctionType({
779
+ type: interfaceName,
780
+ params: ['id: string', 'query?: Q'],
781
+ generics: ['Q extends UniqueQuery'],
782
+ returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
783
+ });
784
+ return {
785
+ entity,
786
+ name: functionName$1,
787
+ type: { name: interfaceName, source: interfaceSource },
788
+ func: { name: functionName$1, source: functionSource }
789
+ };
790
+ };
791
+
792
+ const functionName = 'update';
793
+ const generateUpdateEndpoint = ({ target, path, endpoint }) => {
794
+ const entity = pascalCase(endpoint.entity);
795
+ const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
796
+ const interfaceSource = generateArrowFunctionType({
797
+ type: interfaceName,
798
+ params: ['id: string', `data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'options?: UpdateQuery'],
799
+ returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
800
+ });
801
+ const functionSource = generateArrowFunction({
802
+ name: functionName,
803
+ signature: interfaceName,
804
+ returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options)`,
805
+ params: ['id', 'data', 'options']
806
+ });
807
+ return {
808
+ entity,
809
+ name: functionName,
810
+ type: { name: interfaceName, source: interfaceSource },
811
+ func: { name: functionName, source: functionSource }
812
+ };
813
+ };
814
+
815
+ const groupEndpointsByEntity = (paths) => {
816
+ const endpoints = new Map();
817
+ for (const [rawPath, path] of Object.entries(paths)) {
818
+ const endpoint = parseEndpointPath(rawPath);
819
+ if (!endpoint || !path) {
820
+ logger.errorLn(`Failed to parse ${rawPath}`);
821
+ continue;
822
+ }
823
+ if (endpoints.has(endpoint.entity)) {
824
+ endpoints.get(endpoint.entity)?.push({ endpoint, path });
825
+ }
826
+ else {
827
+ endpoints.set(endpoint.entity, [{ endpoint, path }]);
828
+ }
829
+ }
830
+ return endpoints;
831
+ };
832
+
833
+ const generators = {
834
+ /* /article */
835
+ [WeclappEndpointType.ROOT]: {
836
+ get: generateSomeEndpoint,
837
+ post: generateCreateEndpoint
838
+ },
839
+ /* /article/count */
840
+ [WeclappEndpointType.COUNT]: {
841
+ get: generateCountEndpoint
842
+ },
843
+ /* /article/:id */
844
+ [WeclappEndpointType.ENTITY]: {
845
+ get: generateUniqueEndpoint,
846
+ delete: generateRemoveEndpoint,
847
+ put: generateUpdateEndpoint
848
+ },
849
+ /* /article/:id/method */
850
+ [WeclappEndpointType.GENERIC_ENTITY]: {
851
+ get: generateGenericEndpoint('ById'),
852
+ post: generateGenericEndpoint('ById')
853
+ },
854
+ /* /article/method */
855
+ [WeclappEndpointType.GENERIC_ROOT]: {
856
+ get: generateGenericEndpoint(),
857
+ post: generateGenericEndpoint()
858
+ }
859
+ };
860
+ const generateServices = (doc, aliases, options) => {
861
+ const services = new Map();
862
+ const grouped = groupEndpointsByEntity(doc.paths);
863
+ for (const [endpoint, paths] of grouped) {
864
+ const serviceName = camelCase(`${endpoint}Service`);
865
+ const serviceTypeName = pascalCase(`${endpoint}Service`);
866
+ // Service functions
867
+ const functions = [];
868
+ for (const { path, endpoint } of paths) {
869
+ const resolver = generators[endpoint.type];
870
+ for (const [method, config] of Object.entries(path)) {
871
+ if (method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique) {
872
+ continue;
873
+ }
874
+ if (resolver[method]) {
875
+ const path = config;
876
+ const target = options.target;
877
+ if (!path.deprecated || options.deprecated) {
878
+ functions.push({
879
+ ...resolver[method]({ endpoint, method, target, path, aliases }),
880
+ path
881
+ });
882
+ }
883
+ }
884
+ else {
885
+ logger.errorLn(`Failed to generate a function for ${method.toUpperCase()}:${endpoint.type} ${endpoint.path}`);
886
+ }
887
+ }
888
+ }
889
+ if (!functions.length) {
890
+ continue;
891
+ }
892
+ // Construct service type
893
+ const types = generateStatements(...functions.flatMap(v => v.interfaces?.map(v => v.source) ?? []), ...functions.map(v => v.type.source), generateInterface(serviceTypeName, [
894
+ ...functions.map(v => ({
895
+ required: true,
896
+ comment: v.path.deprecated ? '@deprecated' : undefined,
897
+ name: v.func.name,
898
+ type: v.type.name
899
+ }))
900
+ ]));
901
+ // Construct service value
902
+ const funcBody = generateBlockStatements(...functions.map(v => v.func.source), `return {${concat(functions.map(v => v.func.name))}};`);
903
+ const func = `export const ${serviceName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${funcBody};`;
904
+ const source = generateBlockComment(`${pascalCase(endpoint)} service`, generateStatements(types, func));
905
+ const deprecated = functions.every(v => v.path.deprecated);
906
+ services.set(endpoint, { entity: endpoint, deprecated, serviceName, serviceTypeName, source, functions });
907
+ }
908
+ return services;
909
+ };
910
+
911
+ const generateCustomValueUtilities = (entities, services) => {
912
+ const customValueEntity = entities.get('customValue');
913
+ const customValueEntities = [];
914
+ if (!customValueEntity) {
915
+ logger.warn('Cannot generate custom value utils, type not found.');
916
+ return '';
917
+ }
918
+ serviceLoop: for (const service of services) {
919
+ const someFunction = service.functions.find(v => v.name === 'some');
920
+ if (!someFunction) {
921
+ continue;
922
+ }
923
+ const entity = entities.get(camelCase(someFunction.entity));
924
+ if (entity?.properties.size !== customValueEntity.properties.size) {
925
+ continue;
926
+ }
927
+ for (const [prop, { type }] of entity.properties) {
928
+ if (customValueEntity.properties.get(prop)?.type !== type) {
929
+ continue serviceLoop;
930
+ }
931
+ }
932
+ customValueEntities.push(service.entity);
933
+ }
934
+ return generateBlockComment('Utilities to identify services that return an entity that is an alias to CustomValue.', 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);')}`));
935
+ };
936
+
937
+ const generateObject = (properties) => {
938
+ const body = [];
939
+ for (const { key, value } of properties) {
940
+ if (value === undefined) {
941
+ continue;
942
+ }
943
+ if (Array.isArray(value)) {
944
+ const str = generateObject(value);
945
+ if (str.length > 2) {
946
+ body.push(`${key}: ${str}`);
947
+ }
948
+ }
949
+ else {
950
+ body.push(`${key}: ${String(value)}`);
951
+ }
952
+ }
953
+ return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
954
+ };
955
+
956
+ const resolveInheritedEntities = (root, entities) => {
957
+ const parent = root.extends ? entities.get(root.extends) : undefined;
958
+ return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
959
+ };
960
+ const generatePropertyDescriptors = (entity, entities, services, options) => [
961
+ ...resolveInheritedEntities(entity, entities).flatMap(v => [...v.properties]),
962
+ ...entity.properties
963
+ ].filter(([, meta]) => {
964
+ // If we generate deprecated things we can skip the filtering
965
+ if (options.deprecated) {
966
+ return true;
967
+ }
968
+ // Check if corresponding service is deprecated and can be removed
969
+ const service = services.find(v => v.entity === meta.service);
970
+ return !meta.service || (service && !service.deprecated);
971
+ }).map(([property, meta]) => ({
972
+ key: property,
973
+ value: Object.entries(meta).map(([key, value]) => ({
974
+ key,
975
+ value: value ? generateString(value) : undefined
976
+ }))
977
+ }));
978
+ const generateEntityPropertyMap = (entities, services, options) => {
979
+ const typeName = 'WEntityProperties';
980
+ const propertyMap = [...entities].map(([entity, data]) => ({
981
+ key: entity,
982
+ value: generatePropertyDescriptors(data, entities, services, options)
983
+ }));
984
+ return generateStatements(`export type ${typeName} = Partial<Record<WEntity, Partial<Record<string, WEntityPropertyMeta>>>>;`, `export const wEntityProperties: ${typeName} = ${generateObject(propertyMap)};`);
985
+ };
986
+
987
+ const generateArray = (values) => {
988
+ return `[${concat(values.map(v => generateString(String(v))))}]`;
989
+ };
990
+
991
+ // Only functions matching this regex are included in the generation.
992
+ const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
993
+ /**
994
+ * Generates for each function a map with the entity-name as key and service type as value.
995
+ * E.g. WServicesWith[Function] where [Function] may be something like "some" or "create".
996
+ *
997
+ * This function also generates an exported array with the names of each service for each name.
998
+ */
999
+ const generateGroupedServices = (services) => {
1000
+ const entityDescriptors = new Map();
1001
+ for (const { entity, functions } of services) {
1002
+ for (const { name } of functions) {
1003
+ if (!FILTER_REGEX.test(name)) {
1004
+ continue;
1005
+ }
1006
+ entityDescriptors.set(name, [
1007
+ ...(entityDescriptors.get(name) ?? []), {
1008
+ name: entity,
1009
+ required: true,
1010
+ type: `${pascalCase(entity)}Service_${pascalCase(name)}`
1011
+ }
1012
+ ]);
1013
+ }
1014
+ }
1015
+ const descriptors = [...entityDescriptors.entries()];
1016
+ const typeGuards = [];
1017
+ for (const [name] of descriptors) {
1018
+ const constant = camelCase(`wServiceWith_${name}_Names`);
1019
+ const service = pascalCase(`WServiceWith_${name}`);
1020
+ const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
1021
+ typeGuards.push(`export const is${service} = ${guard}`);
1022
+ }
1023
+ return [
1024
+ ...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)),
1025
+ ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)),
1026
+ ...descriptors.map(([name, props]) => {
1027
+ const constant = camelCase(`wServiceWith_${name}_Names`);
1028
+ const type = pascalCase(`WServiceWith_${name}`);
1029
+ const value = generateArray(props.map(v => v.name));
1030
+ return `export const ${constant}: ${type}[] = ${value};`;
1031
+ }),
1032
+ generateBlockComment('Type guards for service classes.', generateStatements(...typeGuards))
1033
+ ];
1034
+ };
1035
+
1036
+ const obj = (list) => `{\n${indent(list.join(',\n'))}\n}`;
1037
+ const arr = (list) => `[\n${indent(list.join(',\n'))}\n]`;
1038
+ const generateMaps = ({ services, entities, aliases, enums, options }) => {
1039
+ const entitiesKeys = [...entities.keys()];
1040
+ const enumsArray = `export const wEnums = ${obj(enums)};`;
1041
+ const entityNames = `export const wEntityNames: WEntity[] = ${arr(entitiesKeys.map(v => `'${v}'`))};`;
1042
+ const serviceNames = `export const wServiceNames: WService[] = ${arr(services.map(v => `'${v.entity}'`))};`;
1043
+ const serviceValues = `export const wServiceFactories = ${obj(services.map(v => `${v.entity}: ${v.serviceName}`))};`;
1044
+ const serviceInstanceValues = `export const wServices = ${obj(services.map(v => {
1045
+ const src = `${v.entity}: ${v.serviceName}()`;
1046
+ return v.deprecated ? generateInlineComment('@deprecated') + `\n${src}` : src;
1047
+ }))};`;
1048
+ const entityInterfaces = [
1049
+ ...entitiesKeys.map(entity => ({
1050
+ name: entity,
1051
+ type: pascalCase(entity),
1052
+ required: true
1053
+ })),
1054
+ ...services.map(service => {
1055
+ const alias = aliases.get(service.entity);
1056
+ return {
1057
+ name: service.entity,
1058
+ type: alias ?? 'never',
1059
+ required: true,
1060
+ comment: alias ? undefined : 'no response defined or inlined'
1061
+ };
1062
+ })
1063
+ ];
1064
+ const createMappingType = (type, prefix) => type !== 'never' ? `${type}_${prefix}` : type;
1065
+ const entitiesList = generateInterface('WEntities', entityInterfaces);
1066
+ const entityReferences = generateInterface('WEntityReferences', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'References') })));
1067
+ const entityMappings = generateInterface('WEntityMappings', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Mappings') })));
1068
+ const entityFilter = generateInterface('WEntityFilters', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Filter') })));
1069
+ return {
1070
+ source: generateStatements(
1071
+ /* JS Values */
1072
+ serviceValues, serviceInstanceValues, entityNames, serviceNames, enumsArray, generateEntityPropertyMap(entities, services, options),
1073
+ /* Map of entity to references / mappings and filters*/
1074
+ entityReferences, entityMappings, entityFilter,
1075
+ /* List of all entities with their corresponding service */
1076
+ generateBlockComment(`
1077
+ This interfaces merges two maps:
1078
+ - Map<[entityName], [entityInterfaceName]>
1079
+ - Map<[serviceName], [entityInterfaceName]>
1080
+
1081
+ Where [entityName] is
1082
+ - the name of a nested entity (e.g. 'address' from Party)
1083
+ - the name of an entity (e.g. 'party', 'article' etc.)
1084
+
1085
+ Where [serviceName] is the name of an endpoint (e.g. for /article its 'article')
1086
+
1087
+ Where [entityInterfaceName] is
1088
+ - the underlying type for this entity
1089
+ - the type for what is returned by the api
1090
+ `, entitiesList),
1091
+ /* type-ofs and types */
1092
+ generateType('WServices', 'typeof wServices'), generateType('WServiceFactories', 'typeof wServiceFactories'), generateType('WService', 'keyof WServices'), generateType('WEntity', 'keyof WEntities'), generateType('WEnums', 'typeof wEnums'), generateType('WEnum', 'keyof WEnums'),
1093
+ /* Utilities. */
1094
+ generateCustomValueUtilities(entities, services),
1095
+ /* All functions grouped by service supporting it */
1096
+ ...generateGroupedServices(services))
1097
+ };
1098
+ };
1099
+
1100
+ const parseReferencedEntity = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
1101
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
1102
+ const extractSchemas = (doc) => {
1103
+ const schemas = new Map();
1104
+ const aliases = new Map();
1105
+ for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
1106
+ if (!isReferenceObject(schema)) {
1107
+ schemas.set(name, schema);
1108
+ }
1109
+ }
1110
+ /**
1111
+ * Referenced schemas in responses, in some case the response from the root endpoint
1112
+ * refers to a schema with a different name
1113
+ */
1114
+ for (const [path, methods] of Object.entries(doc.paths)) {
1115
+ const parsed = parseEndpointPath(path);
1116
+ if (!parsed || schemas.has(parsed.entity)) {
1117
+ continue;
1118
+ }
1119
+ for (const method of Object.values(OpenAPIV3.HttpMethods)) {
1120
+ const body = methods[method]?.responses['200'];
1121
+ if (isResponseObject(body) && body.content) {
1122
+ const responseSchema = Object.values(body.content)[0]?.schema;
1123
+ if (isReferenceObject(responseSchema)) {
1124
+ continue;
1125
+ }
1126
+ const itemsSchema = responseSchema?.properties?.result;
1127
+ if (isReferenceObject(itemsSchema)) {
1128
+ aliases.set(parsed.entity, parseReferencedEntity(itemsSchema));
1129
+ continue;
1130
+ }
1131
+ if (isArraySchemaObject(itemsSchema)) {
1132
+ const { items } = itemsSchema;
1133
+ if (isReferenceObject(items)) {
1134
+ aliases.set(parsed.entity, parseReferencedEntity(items));
1135
+ }
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ return { schemas, aliases };
1141
+ };
1142
+
1143
+ const generate = (doc, options) => {
1144
+ const { schemas, aliases } = extractSchemas(doc);
1145
+ const enums = generateEnums(schemas);
1146
+ const entities = generateEntities(schemas, enums);
1147
+ const services = generateServices(doc, aliases, options);
1148
+ return generateStatements(generateBase(options.target), generateBlockComment('ENUMS', generateStatements(...[...enums.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', generateMaps({
1149
+ services: [...services.values()],
1150
+ enums: [...enums.keys()],
1151
+ options,
1152
+ entities,
1153
+ aliases
1154
+ }).source));
1155
+ };
1156
+
1157
+ const hash = (content, algorithm = 'sha256') => {
1158
+ const hash = createHash(algorithm);
1159
+ if (Array.isArray(content)) {
1160
+ content.map(hash.update.bind(hash));
1161
+ }
1162
+ else {
1163
+ hash.update(content);
1164
+ }
1165
+ return hash.digest('hex');
1166
+ };
1167
+
1168
+ var name = "@weclapp/sdk";
1169
+ var version = "^1.8.0";
1170
+ var description = "SDK generator based on a weclapp api swagger file";
1171
+ var author = "weclapp";
1172
+ var sideEffects = false;
1173
+ var bin = {
1174
+ "build-weclapp-sdk": "./bin/cli.js"
1175
+ };
1176
+ var files = [
1177
+ "bin",
1178
+ "dist",
1179
+ "tsconfig.lib.json"
1180
+ ];
1181
+ var engines = {
1182
+ node: "^18 || ^20",
1183
+ npm: "^9 || ^8"
1184
+ };
1185
+ var types = "./sdk/dist/index.d.ts";
1186
+ var main = "./sdk/dist/index.cjs";
1187
+ var module = "./sdk/dist/index.js";
1188
+ var type = "module";
1189
+ var exports = {
1190
+ ".": {
1191
+ types: "./sdk/dist/index.d.ts",
1192
+ "import": "./sdk/dist/index.js",
1193
+ require: "./sdk/dist/index.cjs"
1194
+ }
1195
+ };
1196
+ var scripts = {
1197
+ "cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js",
1198
+ "cli:watch": "cross-env NODE_ENV=development rollup -c rollup.config.js --watch",
1199
+ "sdk:build": "./bin/cli.js test/openapi.json --target node",
1200
+ lint: "eslint {src,test}/**/*.ts",
1201
+ "lint:fix": "npm run lint -- --fix",
1202
+ "ci:test": "npm run lint:fix && npm run cli:build && npm run sdk:build",
1203
+ release: "standard-version"
1204
+ };
1205
+ var repository = {
1206
+ type: "git",
1207
+ url: "git@git.internal.weclapp.com:weclapp/weclapp-api/sdk-generator.git"
1208
+ };
1209
+ var devDependencies = {
1210
+ "@typescript-eslint/eslint-plugin": "^5.59.11",
1211
+ "@typescript-eslint/parser": "^5.59.11",
1212
+ eslint: "^8.42.0",
1213
+ "rollup-plugin-string": "^3.0.0",
1214
+ "standard-version": "^9.5.0"
1215
+ };
1216
+ var dependencies = {
1217
+ "@rollup/plugin-json": "^6.0.0",
1218
+ "@rollup/plugin-terser": "^0.4.3",
1219
+ "@types/fs-extra": "^11.0.1",
1220
+ "@types/yargs": "^17.0.24",
1221
+ chalk: "^5.2.0",
1222
+ "change-case": "^4.1.2",
1223
+ "cross-env": "^7.0.3",
1224
+ dotenv: "^16.1.4",
1225
+ "indent-string": "^5.0.0",
1226
+ "openapi-types": "^12.1.3",
1227
+ "pretty-ms": "^8.0.0",
1228
+ rollup: "^3.25.1",
1229
+ "rollup-plugin-ts": "^3.2.0",
1230
+ typescript: "^5.1.3",
1231
+ yargs: "^17.7.2"
1232
+ };
1233
+ var peerDependencies = {
1234
+ rxjs: "^7.5.5"
1235
+ };
1236
+ var pkg = {
1237
+ name: name,
1238
+ version: version,
1239
+ description: description,
1240
+ author: author,
1241
+ sideEffects: sideEffects,
1242
+ bin: bin,
1243
+ files: files,
1244
+ engines: engines,
1245
+ types: types,
1246
+ main: main,
1247
+ module: module,
1248
+ type: type,
1249
+ exports: exports,
1250
+ scripts: scripts,
1251
+ repository: repository,
1252
+ devDependencies: devDependencies,
1253
+ dependencies: dependencies,
1254
+ peerDependencies: peerDependencies
1255
+ };
1256
+
1257
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
1258
+ const cli = async () => {
1259
+ const { argv } = yargs(hideBin(process.argv))
1260
+ .scriptName('build-weclapp-sdk')
1261
+ .usage('Usage: $0 <source> [flags]')
1262
+ .version(version)
1263
+ .example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
1264
+ .example('$0 xxx.weclapp.com --key ...', 'Generate the SDK based on the openapi file from the given weclapp instance')
1265
+ .help('h')
1266
+ .alias('v', 'version')
1267
+ .alias('h', 'help')
1268
+ .option('k', {
1269
+ alias: 'key',
1270
+ describe: 'API Key (only needed when not using a local file)',
1271
+ type: 'string'
1272
+ })
1273
+ .option('c', {
1274
+ alias: 'cache',
1275
+ describe: 'If the generated SDK should cached',
1276
+ type: 'boolean'
1277
+ })
1278
+ .option('q', {
1279
+ alias: 'query',
1280
+ describe: 'Extra query params when fetching the openapi.json from a server',
1281
+ type: 'string'
1282
+ })
1283
+ .option('generate-unique', {
1284
+ describe: 'Generate .unique functions',
1285
+ type: 'boolean'
1286
+ })
1287
+ .option('d', {
1288
+ alias: 'deprecated',
1289
+ describe: 'Include deprecated functions and services',
1290
+ type: 'boolean'
1291
+ })
1292
+ .option('e', {
1293
+ alias: 'from-env',
1294
+ describe: 'Use env variables WECLAPP_BACKEND_URL and WECLAPP_API_KEY as credentials',
1295
+ type: 'boolean'
1296
+ })
1297
+ .option('t', {
1298
+ alias: 'target',
1299
+ describe: 'Specify the target platform',
1300
+ type: 'string',
1301
+ choices: ['browser', 'browser.rx', 'node', 'node.rx']
1302
+ })
1303
+ .option('d', {
1304
+ alias: 'deprecated',
1305
+ describe: 'Include deprecated functions and services',
1306
+ type: 'boolean'
1307
+ })
1308
+ .epilog(`Copyright ${new Date().getFullYear()} weclapp GmbH`);
1309
+ if (argv.fromEnv) {
1310
+ config();
1311
+ }
1312
+ const { WECLAPP_API_KEY, WECLAPP_BACKEND_URL } = process.env;
1313
+ const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, _: [src = WECLAPP_BACKEND_URL] } = argv;
1314
+ const options = {
1315
+ deprecated,
1316
+ generateUnique: argv.generateUnique ?? false,
1317
+ target: argv.target ?? Target.BROWSER_PROMISES
1318
+ };
1319
+ if (typeof src === 'number') {
1320
+ return Promise.reject('Expected string as command');
1321
+ }
1322
+ if (!Object.values(Target).includes(options.target)) {
1323
+ logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(', ')}`);
1324
+ return Promise.reject();
1325
+ }
1326
+ if (await stat(src).catch(() => false)) {
1327
+ logger.infoLn(`Source is a file`);
1328
+ const content = JSON.parse(await readFile(src, 'utf-8'));
1329
+ return { cache, content, options };
1330
+ }
1331
+ const url = new URL(src.startsWith('http') ? src : `https://${src}`);
1332
+ url.pathname = '/webapp/api/v1/meta/openapi.json';
1333
+ if (query?.length) {
1334
+ for (const param of query.split(',')) {
1335
+ const [name, value] = param.split('=');
1336
+ url.searchParams.set(name, value);
1337
+ }
1338
+ }
1339
+ const content = await fetch(url.toString(), {
1340
+ headers: { 'Accept': 'application/json', 'AuthenticationToken': key }
1341
+ }).then(res => res.ok ? res.json() : undefined);
1342
+ if (!content) {
1343
+ logger.errorLn(`Couldn't fetch file ${url.toString()} `);
1344
+ return Promise.reject();
1345
+ }
1346
+ else {
1347
+ logger.infoLn(`Use remote file: ${url.toString()}`);
1348
+ }
1349
+ return { cache, content, options };
1350
+ };
1351
+
1352
+ const workingDirectory = resolve(currentDirname(), './sdk');
1353
+ const folders = ['docs', 'main', 'node', 'raw', 'rx', 'utils'];
1354
+ void (async () => {
1355
+ const start = process.hrtime.bigint();
1356
+ const { content: doc, cache: useCache, options } = await cli();
1357
+ // Resolve cache dir and key
1358
+ const cacheKey = hash([pkg.version, JSON.stringify(doc), JSON.stringify(options)]).slice(-8);
1359
+ const cacheDir = resolve(currentDirname(), '.tmp', cacheKey);
1360
+ const dist = (...paths) => resolve(workingDirectory, ...paths);
1361
+ const tmp = async (...paths) => {
1362
+ const fullPath = resolve(cacheDir, ...paths);
1363
+ await mkdir(dirname(fullPath), { recursive: true }).catch(() => null);
1364
+ return fullPath;
1365
+ };
1366
+ if (useCache) {
1367
+ logger.infoLn(`Cache ID: ${cacheKey}`);
1368
+ }
1369
+ if (useCache && await stat(cacheDir).catch(() => false)) {
1370
+ logger.successLn(`Cache match! (${cacheDir})`);
1371
+ }
1372
+ else {
1373
+ // Store swagger.json file
1374
+ await writeFile(await tmp('openapi.json'), JSON.stringify(doc, null, 2));
1375
+ logger.infoLn(`Generate sdk (target: ${options.target})`);
1376
+ // Generate SDKs
1377
+ const sdk = generate(doc, options);
1378
+ await writeFile(await tmp('src', `${options.target}.ts`), sdk.trim() + '\n');
1379
+ // Bundle
1380
+ logger.infoLn('Bundle... (this may take some time)');
1381
+ await bundle(cacheDir, options.target);
1382
+ // Remove old SDK
1383
+ await Promise.all(folders.map(async (dir) => rm(dist(dir), { recursive: true }).catch(() => 0)));
1384
+ }
1385
+ // Copy bundled SDK
1386
+ await cp(cacheDir, workingDirectory, { recursive: true });
1387
+ // Print job summary
1388
+ const duration = (process.hrtime.bigint() - start) / 1000000n;
1389
+ logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
1390
+ logger.printSummary();
1391
+ })().catch((error) => {
1392
+ logger.errorLn(`Fatal error:`);
1393
+ /* eslint-disable no-console */
1394
+ console.error(error);
1395
+ }).finally(() => {
1396
+ logger.errors && process.exit(1);
1397
+ });