@weclapp/sdk 2.0.0-dev.30 → 2.0.0-dev.31

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.
Files changed (2) hide show
  1. package/dist/cli.js +347 -354
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { rollup } from 'rollup';
4
4
  import terser from '@rollup/plugin-terser';
5
5
  import ts from '@rollup/plugin-typescript';
6
6
  import indentString from 'indent-string';
7
- import { snakeCase, pascalCase, camelCase } from 'change-case';
7
+ import { snakeCase, camelCase, pascalCase } from 'change-case';
8
8
  import chalk from 'chalk';
9
9
  import { OpenAPIV3 } from 'openapi-types';
10
10
  import { createHash } from 'crypto';
@@ -15,6 +15,11 @@ import { hideBin } from 'yargs/helpers';
15
15
  import pkg from '../package.json' with { type: 'json' };
16
16
  import prettyMs from 'pretty-ms';
17
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
+
18
23
  var Target;
19
24
  (function (Target) {
20
25
  Target["BROWSER_PROMISES"] = "browser";
@@ -35,11 +40,6 @@ const resolveBinaryType = (target) => {
35
40
  return isNodeTarget(target) ? 'Buffer' : 'Blob';
36
41
  };
37
42
 
38
- const currentDirname = () => {
39
- // Go one level up as the CLI is inside a folder
40
- return fileURLToPath(new URL('..', import.meta.url));
41
- };
42
-
43
43
  const tsconfig = resolve(currentDirname(), './tsconfig.sdk.json');
44
44
  const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, '*']));
45
45
  const generateOutput = (config) => ({
@@ -134,15 +134,19 @@ const generateBlockStatements = (...statements) => `{\n${indent(generateStatemen
134
134
 
135
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\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
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 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";
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 MapOperators<T> = { [K in EqualityOperator]?: T | null } & { [K in ComparisonOperator]?: T } & {\n [K in ArrayOperator]?: 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) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\n }\n })\n );\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> }\n) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n query: {\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 }).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\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 MapOperators<T> =\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: T[] } & {\n [K in NullOperator]?: never } & {\n [K in ModifierFunction]?: boolean\n })\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: 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> & CustomAttributeFilter;\n};\n\nexport type SomeQuery<E, F, I, P> = {\n serializeNulls?: boolean;\n include?: QuerySelect<I>;\n properties?: P;\n where?: QueryFilter<F> & CustomAttributeFilter;\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) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...assembleFilterParam(query?.where),\n ...query?.params\n }\n })\n );\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> }\n) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n query: {\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 }).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\n );\n";
138
142
 
139
- var queriesWithFilter = "// Filter properties\nexport type EqualityOperators = 'EQ' | 'NE';\nexport type ComparisonOperators =\n | 'LT'\n | 'GT'\n | 'LE'\n | 'GE'\n | 'LIKE'\n | 'ILIKE'\n | 'NOT_LIKE'\n | 'NOT_ILIKE'\n | 'IEQ'\n | 'NOT_IEQ';\nexport type ArrayOperators = 'IN' | 'NOT_IN';\nexport type Operator = EqualityOperators | ComparisonOperators | ArrayOperators;\n\nexport type MapOperators<T> = { [K in EqualityOperators]?: T | null } & { [K in ComparisonOperators]?: T } & {\n [K in ArrayOperators]?: 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\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\n// Endpoint configurations\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\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 P extends string[] // Select for additional properties\n> = {\n serializeNulls?: boolean;\n include?: I;\n properties?: P;\n filter?: QueryFilter<F> & CustomAttributeFilter;\n select?: S;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & { params?: Record<any, any> }\n) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\n }\n })\n );\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any, any> & { params?: Record<any, any> }\n) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n query: {\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 }).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\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";
143
+ var root = "export const raw = async (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n payload: RequestPayload = {}\n): Promise<any> => {\n const globalConfig = getGlobalConfig();\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(\n Object.entries(payload.query ?? {})\n .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 = 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) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'DELETE',\n query: { dryRun }\n }).then(() => undefined)\n );\n\nconst _create = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, data: any) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'POST',\n body: data\n })\n );\n\nconst _update = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n data: any,\n { ignoreMissingProperties, dryRun = false }: UpdateQuery = {}\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 })\n );\n\nconst _generic = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n method: RequestPayloadMethod,\n endpoint: string,\n payload?: GenericQuery<any, any>,\n forceBlob?: boolean\n) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n })\n );\n";
140
144
 
141
- var queriesWithQueryLanguage = "// New filter language\n\nexport type ComparisonOperators = 'EQ' | 'NE' | 'LT' | 'GT' | 'LE' | 'GE' | 'LIKE';\nexport type ArrayOperators = 'IN';\nexport type NullOperator = 'NULL';\nexport type Operator = ComparisonOperators | ArrayOperators | NullOperator;\n\nconst comparisonOperatorsList: ComparisonOperators[] = ['EQ', 'NE', 'LT', 'GT', 'LE', 'GE', 'LIKE'];\n\nconst filterMap: 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\n// Maybe we need more in the future, hence the type.\nexport type ModifierFunction = 'lower';\n\nexport type MapOperators<T> =\n | ({ [K in ComparisonOperators]?: T } & { [K in ArrayOperators]?: T[] } & { [K in NullOperator]?: never } & {\n [K in ModifierFunction]?: boolean;\n })\n | ({ [K in ComparisonOperators]?: T } & { [K in ArrayOperators]?: T[] } & { [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\n// Endpoint configurations\nexport type CountQuery<F> = {\n where?: QueryFilter<F> & CustomAttributeFilter;\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 P extends string[] // Select for additional properties\n> = {\n serializeNulls?: boolean;\n include?: I;\n properties?: P;\n where?: QueryFilter<F> & CustomAttributeFilter;\n select?: S;\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst possibleModifierFunctions: ModifierFunction[] = ['lower'];\n\nconst flattenWhere = (obj: QueryFilter<any> = {}, nestedPaths: string[]): string[] => {\n const entries: string[] = [];\n for (const [prop, propValue] of Object.entries(obj)) {\n const setModifiers = findAllModifierFunctions(propValue ?? {}, possibleModifierFunctions).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 (comparisonOperatorsList.includes(operator as ComparisonOperators)) {\n entries.push(\n `${setModifiers.reduce(\n (acc, [first]) => `${first}(${acc})`,\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\n )} ${filterMap[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('.')} ${filterMap[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 )} ${filterMap[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 (!possibleModifierFunctions.includes(operator as ModifierFunction)) {\n entries.push(...flattenWhere(propValue as QueryFilter<any>, [...nestedPaths, prop]));\n break;\n }\n }\n }\n }\n return entries;\n};\n\nconst assembleFilterParam = (obj: QueryFilter<any> = {}): Record<string, string> => {\n const flattedFilter = flattenWhere(obj, []);\n return flattedFilter.length ? { filter: flattedFilter.join(' and ') } : {};\n};\n\nconst findAllModifierFunctions = (obj: Record<string, any>, types: ModifierFunction[]) => {\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) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...assembleFilterParam(query?.where),\n ...query?.params\n }\n })\n );\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any, any> & { params?: Record<any, any> }\n) =>\n wrapResponse(() =>\n raw(cfg, endpoint, {\n query: {\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 ...assembleFilterParam(query?.where),\n ...flattenSort(query?.sort),\n ...query?.params,\n ...query?.pagination\n }\n }).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\n );\n";
145
+ var unique = "const _unique = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, query?: UniqueQuery) =>\n wrapResponse(() => raw({ ...cfg, multiRequest: false }, endpoint, { query }));\n";
142
146
 
143
- var root = "export const raw = async (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n payload: RequestPayload = {}\n): Promise<any> => {\n const globalConfig = getGlobalConfig();\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(\n Object.entries(payload.query ?? {})\n .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: any = undefined;\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 = 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) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'DELETE',\n query: { dryRun }\n }).then(() => undefined)\n );\n\nconst _create = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, data: any) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method: 'POST',\n body: data\n })\n );\n\nconst _update = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n data: any,\n { ignoreMissingProperties, dryRun = false }: UpdateQuery = {}\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 })\n );\n\nconst _unique = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, query?: UniqueQuery) =>\n wrapResponse(() => raw({ ...cfg, multiRequest: false }, endpoint, { query }));\n\nconst _generic = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n method: RequestPayloadMethod,\n endpoint: string,\n payload?: GenericQuery<any, any>,\n forceBlob?: boolean\n) =>\n wrapResponse(() =>\n raw({ ...cfg, multiRequest: false }, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n })\n );\n";
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";
144
148
 
145
- 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\n// Select properties\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 Pagination = {\n page: number;\n pageSize: number;\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 P // Additional 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 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\n// Entity meta types\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 | { 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// Utils\nconst equality: string[] = ['EQ', 'NE', 'IEQ', 'NOT_IEQ'];\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\nconst array: string[] = ['IN', 'NOT_IN'];\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";
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";
146
150
 
147
151
  const resolveImports = (target) => {
148
152
  const imports = [];
@@ -153,8 +157,8 @@ const resolveImports = (target) => {
153
157
  };
154
158
  const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
155
159
  const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
156
- const generateBase = (target, apiVersion, useQueryLanguage) => {
157
- return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), `const apiVersion = ${apiVersion}`, globalConfig, multiRequest, useQueryLanguage ? queriesWithQueryLanguage : queriesWithFilter, 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);
158
162
  };
159
163
 
160
164
  const transformKey = (s) => snakeCase(s).toUpperCase();
@@ -196,13 +200,14 @@ const isRelatedEntitySchema = (v) => {
196
200
 
197
201
  const generateEnums = (schemas) => {
198
202
  const enums = new Map();
199
- for (const [propName, schema] of schemas) {
203
+ for (const [schemaName, schema] of schemas) {
200
204
  if (isEnumSchemaObject(schema)) {
201
- const name = loosePascalCase(propName);
202
- if (!enums.has(name)) {
203
- enums.set(name, {
205
+ const enumName = loosePascalCase(schemaName);
206
+ if (!enums.has(enumName)) {
207
+ enums.set(enumName, {
208
+ name: enumName,
204
209
  properties: schema.enum,
205
- source: generateEnum(name, schema.enum)
210
+ source: generateEnum(enumName, schema.enum)
206
211
  });
207
212
  }
208
213
  }
@@ -368,35 +373,13 @@ const generateEntities = (schemas, enums) => {
368
373
  if (isEnumSchemaObject(schema)) {
369
374
  continue;
370
375
  }
371
- const entity = pascalCase(schemaName);
372
- // Entity and filter
373
- const entityInterface = [];
374
- const filterInterface = [];
375
- // Referenced entities and property-to-referenced-entity mapping
376
- const referenceInterface = [];
377
- const referenceMappingsInterface = [];
376
+ const entityInterfaceName = loosePascalCase(schemaName);
377
+ let parentEntityInterfaceName = undefined;
378
+ const entityInterfaceProperties = [];
378
379
  const properties = new Map();
379
- // The parent entity
380
- let extend = undefined;
381
380
  const processProperties = (props = {}) => {
382
381
  for (const [name, property] of Object.entries(props)) {
383
382
  const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
384
- if (meta.entity) {
385
- const type = `${pascalCase(meta.entity)}[]`;
386
- if (schemas.has(meta.entity)) {
387
- referenceInterface.push({ name, type, required: true });
388
- filterInterface.push({ name: meta.entity, type, required: true });
389
- }
390
- }
391
- if (meta.service) {
392
- if (schemas.has(meta.service)) {
393
- referenceMappingsInterface.push({
394
- name,
395
- type: generateString(meta.service),
396
- required: true
397
- });
398
- }
399
- }
400
383
  const type = convertToTypeScriptType(property, name).toString();
401
384
  const comment = isNonArraySchemaObject(property)
402
385
  ? property.deprecated
@@ -405,7 +388,7 @@ const generateEntities = (schemas, enums) => {
405
388
  ? `format: ${property.format}`
406
389
  : undefined
407
390
  : undefined;
408
- entityInterface.push({
391
+ entityInterfaceProperties.push({
409
392
  name,
410
393
  type,
411
394
  comment,
@@ -418,7 +401,7 @@ const generateEntities = (schemas, enums) => {
418
401
  if (schema.allOf?.length) {
419
402
  for (const item of schema.allOf) {
420
403
  if (isReferenceObject(item)) {
421
- extend = convertToTypeScriptType(item).toString();
404
+ parentEntityInterfaceName = convertToTypeScriptType(item).toString();
422
405
  }
423
406
  else if (isObjectSchemaObject(item)) {
424
407
  processProperties(item.properties);
@@ -426,11 +409,11 @@ const generateEntities = (schemas, enums) => {
426
409
  }
427
410
  }
428
411
  processProperties(schema.properties);
429
- 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));
430
412
  entities.set(schemaName, {
431
- extends: extend ? camelCase(extend) : extend,
413
+ name: schemaName,
432
414
  properties,
433
- source
415
+ parentName: parentEntityInterfaceName ? camelCase(parentEntityInterfaceName) : undefined,
416
+ source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName))
434
417
  });
435
418
  }
436
419
  return entities;
@@ -506,10 +489,10 @@ const logger = new (class {
506
489
  })();
507
490
 
508
491
  /**
509
- * ROOT => /article
510
- * COUNT => /article/count
511
- * ENTITY => /article/{id}
512
- * SPECIAL_ROOT => /article/generateImage
492
+ * ROOT => /article
493
+ * COUNT => /article/count
494
+ * ENTITY => /article/{id}
495
+ * SPECIAL_ROOT => /article/generateImage
513
496
  * SPECIAL_ENTITY => /article/id/{id}/generateImag
514
497
  */
515
498
  var WeclappEndpointType;
@@ -521,22 +504,22 @@ var WeclappEndpointType;
521
504
  WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
522
505
  })(WeclappEndpointType || (WeclappEndpointType = {}));
523
506
  const parseEndpointPath = (path) => {
524
- const [, entity, ...rest] = path.split('/');
525
- if (!entity) {
507
+ const [, service, ...rest] = path.split('/');
508
+ if (!service) {
526
509
  return undefined;
527
510
  }
528
511
  if (!rest.length) {
529
- return { path, entity, type: WeclappEndpointType.ROOT };
512
+ return { path, service, type: WeclappEndpointType.ROOT };
530
513
  }
531
514
  else if (rest[0] === 'count') {
532
- return { path, entity, type: WeclappEndpointType.COUNT };
515
+ return { path, service, type: WeclappEndpointType.COUNT };
533
516
  }
534
517
  else if (rest[0] === 'id') {
535
518
  return rest.length === 2
536
- ? { path, entity, type: WeclappEndpointType.ENTITY }
519
+ ? { path, service, type: WeclappEndpointType.ENTITY }
537
520
  : {
538
521
  path,
539
- entity,
522
+ service,
540
523
  method: rest[2],
541
524
  type: WeclappEndpointType.GENERIC_ENTITY
542
525
  };
@@ -544,7 +527,7 @@ const parseEndpointPath = (path) => {
544
527
  else if (rest.length === 1) {
545
528
  return {
546
529
  path,
547
- entity,
530
+ service,
548
531
  method: rest[1],
549
532
  type: WeclappEndpointType.GENERIC_ROOT
550
533
  };
@@ -581,38 +564,38 @@ const convertParametersToSchema = (parameters = []) => {
581
564
  };
582
565
  };
583
566
 
584
- const functionName$5 = 'count';
585
567
  const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
586
- const service = pascalCase(endpoint.entity);
587
- const entity = aliases.get(endpoint.entity) ?? service;
588
- const entityFilter = `${entity}_Filter`;
589
- const interfaceName = `${service}Service_${pascalCase(functionName$5)}`;
590
- const entityParameters = `${interfaceName}_Parameters`;
591
- const parameterSchema = convertParametersToSchema(path.parameters);
592
- const parameters = createObjectType({
593
- params: convertToTypeScriptType(parameterSchema)
568
+ const functionName = 'count';
569
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
570
+ const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
571
+ const parametersTypeName = `${functionTypeName}_Parameters`;
572
+ const parametersType = createObjectType({
573
+ params: convertToTypeScriptType(convertParametersToSchema(path.parameters))
574
+ });
575
+ const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
576
+ const filterTypeName = `${functionTypeName}_Filter`;
577
+ const filterTypeSource = generateInterfaceType(filterTypeName, [], [entity]);
578
+ const functionTypeSource = generateArrowFunctionType({
579
+ type: functionTypeName,
580
+ params: [
581
+ `query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${path.parameters?.length ? ' & ' + parametersTypeName : ''}`
582
+ ],
583
+ returns: `${resolveResponseType(target)}<number>`
594
584
  });
595
585
  const functionSource = generateArrowFunction({
596
- name: functionName$5,
597
- signature: interfaceName,
598
- returns: `_${functionName$5}(cfg, ${generateString(endpoint.path)}, query)`,
586
+ name: functionName,
587
+ signature: functionTypeName,
588
+ returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query)`,
599
589
  params: ['query']
600
590
  });
601
- const interfaceSource = generateArrowFunctionType({
602
- type: interfaceName,
603
- params: [`query${parameters.isFullyOptional() ? '?' : ''}: CountQuery<${entityFilter}> & ${entityParameters}`],
604
- returns: `${resolveResponseType(target)}<number>`
605
- });
606
591
  return {
607
592
  entity,
608
- name: functionName$5,
609
- type: { name: interfaceName, source: interfaceSource },
610
- func: { name: functionName$5, source: functionSource },
593
+ name: functionName,
594
+ type: { name: functionTypeName, source: functionTypeSource },
595
+ func: { name: functionName, source: functionSource },
611
596
  interfaces: [
612
- {
613
- name: entityParameters,
614
- source: generateInterfaceFromObject(entityParameters, parameters, true)
615
- }
597
+ ...(path.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
598
+ { name: filterTypeName, source: filterTypeSource }
616
599
  ]
617
600
  };
618
601
  };
@@ -637,26 +620,25 @@ const generateRequestBodyType = ({ requestBody }) => {
637
620
  const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
638
621
  const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
639
622
 
640
- const functionName$4 = 'create';
641
623
  const generateCreateEndpoint = ({ target, path, endpoint }) => {
642
- const entity = pascalCase(endpoint.entity);
643
- const interfaceName = `${entity}Service_${pascalCase(functionName$4)}`;
644
- const functionSource = generateArrowFunction({
645
- name: functionName$4,
646
- signature: interfaceName,
647
- returns: `_${functionName$4}(cfg, ${generateString(endpoint.path)}, data)`,
648
- params: ['data']
649
- });
650
- const interfaceSource = generateArrowFunctionType({
651
- type: interfaceName,
624
+ const functionName = 'create';
625
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
626
+ const functionTypeSource = generateArrowFunctionType({
627
+ type: functionTypeName,
652
628
  params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`],
653
629
  returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
654
630
  });
631
+ const functionSource = generateArrowFunction({
632
+ name: functionName,
633
+ signature: functionTypeName,
634
+ returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, data)`,
635
+ params: ['data']
636
+ });
655
637
  return {
656
- entity,
657
- name: functionName$4,
658
- type: { name: interfaceName, source: interfaceSource },
659
- func: { name: functionName$4, source: functionSource }
638
+ entity: pascalCase(endpoint.service),
639
+ name: functionName,
640
+ type: { name: functionTypeName, source: functionTypeSource },
641
+ func: { name: functionName, source: functionSource }
660
642
  };
661
643
  };
662
644
 
@@ -673,31 +655,29 @@ const wrapBody = (type, target) => {
673
655
  };
674
656
  const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
675
657
  const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
676
- const entity = pascalCase(endpoint.entity);
677
- const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
678
- const entityQuery = `${interfaceName}_Query`;
658
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
659
+ const entityQuery = `${functionTypeName}_Query`;
679
660
  const hasId = endpoint.path.includes('{id}');
680
661
  const params = createObjectType({
681
662
  params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
682
663
  body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
683
664
  });
684
665
  const responseBody = generateResponseBodyType(path);
685
- const forceBlobResponse = String(responseBody.toString() === 'binary');
666
+ const functionTypeSource = generateArrowFunctionType({
667
+ type: functionTypeName,
668
+ params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
669
+ returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
670
+ });
686
671
  const functionSource = generateArrowFunction({
687
672
  name: functionName,
688
- signature: interfaceName,
673
+ signature: functionTypeName,
689
674
  params: hasId ? ['id', 'query'] : ['query'],
690
- returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${forceBlobResponse})`
691
- });
692
- const interfaceSource = generateArrowFunctionType({
693
- type: interfaceName,
694
- params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
695
- returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
675
+ returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${String(responseBody.toString() === 'binary')})`
696
676
  });
697
677
  return {
698
- entity,
678
+ entity: pascalCase(endpoint.service),
699
679
  name: functionName,
700
- type: { name: interfaceName, source: interfaceSource },
680
+ type: { name: functionTypeName, source: functionTypeSource },
701
681
  func: { name: functionName, source: functionSource },
702
682
  interfaces: [
703
683
  {
@@ -708,32 +688,38 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
708
688
  };
709
689
  };
710
690
 
711
- const functionName$3 = 'remove';
712
691
  const generateRemoveEndpoint = ({ target, endpoint }) => {
713
- const entity = pascalCase(endpoint.entity);
714
- const interfaceName = `${entity}Service_${pascalCase(functionName$3)}`;
715
- const functionSource = generateArrowFunction({
716
- name: functionName$3,
717
- signature: interfaceName,
718
- returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
719
- params: ['id', 'options?: RemoveQuery']
720
- });
721
- const interfaceSource = generateArrowFunctionType({
722
- type: interfaceName,
692
+ const functionName = 'remove';
693
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
694
+ const functionTypeSource = generateArrowFunctionType({
695
+ type: functionTypeName,
723
696
  params: ['id: string', 'options?: RemoveQuery'],
724
697
  returns: `${resolveResponseType(target)}<void>`
725
698
  });
699
+ const functionSource = generateArrowFunction({
700
+ name: functionName,
701
+ signature: functionTypeName,
702
+ returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
703
+ params: ['id', 'options?: RemoveQuery']
704
+ });
726
705
  return {
727
- entity,
728
- name: functionName$3,
729
- type: { name: interfaceName, source: interfaceSource },
730
- func: { name: functionName$3, source: functionSource }
706
+ entity: pascalCase(endpoint.service),
707
+ name: functionName,
708
+ type: { name: functionTypeName, source: functionTypeSource },
709
+ func: { name: functionName, source: functionSource }
731
710
  };
732
711
  };
733
712
 
734
- const functionName$2 = 'some';
735
- const excludedParameters = ['page', 'pageSize', 'sort', 'serializeNulls', 'properties', 'includeReferencedEntities'];
736
- const resolveAdditionalProperties = (path) => {
713
+ const excludedParameters = [
714
+ 'page',
715
+ 'pageSize',
716
+ 'sort',
717
+ 'serializeNulls',
718
+ 'properties',
719
+ 'includeReferencedEntities',
720
+ 'additionalProperties'
721
+ ];
722
+ const resolveAdditionalPropertiesSchema = (path) => {
737
723
  const body = resolveBodyType(path);
738
724
  if (isResponseObject(body)) {
739
725
  const schema = body?.content?.['application/json']?.schema;
@@ -746,101 +732,136 @@ const resolveAdditionalProperties = (path) => {
746
732
  }
747
733
  return undefined;
748
734
  };
749
- const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
750
- // Required interface names
751
- const service = pascalCase(endpoint.entity);
752
- const entity = aliases.get(endpoint.entity) ?? service;
753
- const interfaceName = `${service}Service_${pascalCase(functionName$2)}`;
754
- const entityFilter = `${entity}_Filter`;
755
- const entityMappings = `${entity}_Mappings`;
756
- const entityReferences = `${entity}_References`;
757
- const entityParameters = `${service}_Parameters`;
758
- const parameterSchema = convertParametersToSchema(path.parameters);
759
- const additionalProperties = resolveAdditionalProperties(path);
760
- const additionalPropertyNames = generateStrings(Object.keys(additionalProperties?.properties ?? {}));
761
- const additionalPropertyNamesType = additionalPropertyNames.length
762
- ? `(${concat(additionalPropertyNames, ' | ')})[]`
763
- : '[]';
764
- // We already cover some properties
765
- parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {}).filter((v) => !excludedParameters.includes(v[0])));
766
- const parameters = createObjectType({
767
- params: convertToTypeScriptType(parameterSchema)
735
+ const resolveReferences = (entity, entities) => {
736
+ const references = [];
737
+ const generatedEntity = entities.get(entity);
738
+ if (generatedEntity) {
739
+ for (const [property, propertyMetaData] of generatedEntity.properties) {
740
+ if (propertyMetaData.service) {
741
+ references.push({
742
+ name: property,
743
+ type: generateString(propertyMetaData.service),
744
+ required: true
745
+ });
746
+ }
747
+ }
748
+ if (generatedEntity.parentName) {
749
+ references.push(...resolveReferences(generatedEntity.parentName, entities));
750
+ }
751
+ }
752
+ return references;
753
+ };
754
+ const resolveReferencedEntities = (entity, entities) => {
755
+ const referencedEntities = [];
756
+ const generatedEntity = entities.get(entity);
757
+ if (generatedEntity) {
758
+ for (const [, propertyMetaData] of generatedEntity.properties) {
759
+ if (propertyMetaData.entity && propertyMetaData.service) {
760
+ referencedEntities.push({
761
+ name: propertyMetaData.service,
762
+ type: `${pascalCase(propertyMetaData.entity)}[]`,
763
+ required: true
764
+ });
765
+ }
766
+ }
767
+ if (generatedEntity.parentName) {
768
+ referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
769
+ }
770
+ }
771
+ return referencedEntities;
772
+ };
773
+ const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) => {
774
+ const functionName = 'some';
775
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
776
+ const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
777
+ const parametersTypeName = `${functionTypeName}_Parameters`;
778
+ const parameters = path.parameters?.filter((v) => (isParameterObject(v) ? !excludedParameters.includes(v.name) : false)) ?? [];
779
+ const parametersType = createObjectType({
780
+ params: convertToTypeScriptType(convertParametersToSchema(parameters))
768
781
  });
769
- const properties = additionalProperties ? convertToTypeScriptType(additionalProperties).toString() : '{}';
770
- const interfaceSource = generateArrowFunctionType({
771
- type: interfaceName,
772
- generics: [
773
- `S extends (QuerySelect<${entity}> | undefined) = undefined`,
774
- `I extends (QuerySelect<${entityMappings}> | undefined) = undefined`
775
- ],
776
- params: [
777
- `query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S, ${additionalPropertyNamesType}> & ${entityParameters}`
778
- ],
779
- returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S, ${properties}>>`
782
+ const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
783
+ const filterTypeName = `${functionTypeName}_Filter`;
784
+ const filterTypeSource = generateInterfaceType(filterTypeName, [], [entity]);
785
+ const referencesTypeName = `${functionTypeName}_References`;
786
+ const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
787
+ const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
788
+ const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, 'string');
789
+ const queryTypeName = `${functionTypeName}_Query`;
790
+ const queryTypeSource = generateType(queryTypeName, `SomeQuery<${entity}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
791
+ const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
792
+ const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
793
+ const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
794
+ const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(path);
795
+ const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : '{}');
796
+ const functionTypeSource = generateArrowFunctionType({
797
+ type: functionTypeName,
798
+ params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}`],
799
+ returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
780
800
  });
781
801
  const functionSource = generateArrowFunction({
782
- name: functionName$2,
783
- signature: interfaceName,
784
- returns: `_${functionName$2}(cfg, ${generateString(endpoint.path)}, query)`,
802
+ name: functionName,
803
+ signature: functionTypeName,
804
+ returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query)`,
785
805
  params: ['query']
786
806
  });
787
807
  return {
788
808
  entity,
789
- name: functionName$2,
790
- type: { name: interfaceName, source: interfaceSource },
791
- func: { name: functionName$2, source: functionSource },
809
+ name: functionName,
810
+ type: { name: functionTypeName, source: functionTypeSource },
811
+ func: { name: functionName, source: functionSource },
792
812
  interfaces: [
793
- {
794
- name: entityParameters,
795
- source: generateInterfaceFromObject(entityParameters, parameters, true)
796
- }
813
+ { name: parametersTypeName, source: parametersTypeSource },
814
+ { name: filterTypeName, source: filterTypeSource },
815
+ { name: referencesTypeName, source: referencesTypeSource },
816
+ { name: additionalPropertyTypeName, source: additionalPropertyTypeSource },
817
+ { name: queryTypeName, source: queryTypeSource },
818
+ { name: referencedEntitiesTypeName, source: referencedEntitiesTypeSource },
819
+ { name: additionalPropertiesTypeName, source: additionalPropertiesTypeSource }
797
820
  ]
798
821
  };
799
822
  };
800
823
 
801
- const functionName$1 = 'unique';
802
824
  const generateUniqueEndpoint = ({ target, path, endpoint }) => {
803
- const entity = pascalCase(endpoint.entity);
804
- const interfaceName = `${entity}Service_${pascalCase(functionName$1)}`;
805
- const functionSource = generateArrowFunction({
806
- name: functionName$1,
807
- signature: interfaceName,
808
- params: ['id', 'query'],
809
- returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
810
- });
811
- const interfaceSource = generateArrowFunctionType({
812
- type: interfaceName,
825
+ const functionName = 'unique';
826
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
827
+ const functionTypeSource = generateArrowFunctionType({
828
+ type: functionTypeName,
813
829
  params: ['id: string', 'query?: Q'],
814
830
  generics: ['Q extends UniqueQuery'],
815
831
  returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
816
832
  });
833
+ const functionSource = generateArrowFunction({
834
+ name: functionName,
835
+ signature: functionTypeName,
836
+ params: ['id', 'query'],
837
+ returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
838
+ });
817
839
  return {
818
- entity,
819
- name: functionName$1,
820
- type: { name: interfaceName, source: interfaceSource },
821
- func: { name: functionName$1, source: functionSource }
840
+ entity: pascalCase(endpoint.service),
841
+ name: functionName,
842
+ type: { name: functionTypeName, source: functionTypeSource },
843
+ func: { name: functionName, source: functionSource }
822
844
  };
823
845
  };
824
846
 
825
- const functionName = 'update';
826
847
  const generateUpdateEndpoint = ({ target, path, endpoint }) => {
827
- const entity = pascalCase(endpoint.entity);
828
- const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
829
- const interfaceSource = generateArrowFunctionType({
830
- type: interfaceName,
848
+ const functionName = 'update';
849
+ const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
850
+ const functionTypeSource = generateArrowFunctionType({
851
+ type: functionTypeName,
831
852
  params: ['id: string', `data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'options?: UpdateQuery'],
832
853
  returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
833
854
  });
834
855
  const functionSource = generateArrowFunction({
835
856
  name: functionName,
836
- signature: interfaceName,
857
+ signature: functionTypeName,
837
858
  returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options)`,
838
859
  params: ['id', 'data', 'options']
839
860
  });
840
861
  return {
841
- entity,
862
+ entity: pascalCase(endpoint.service),
842
863
  name: functionName,
843
- type: { name: interfaceName, source: interfaceSource },
864
+ type: { name: functionTypeName, source: functionTypeSource },
844
865
  func: { name: functionName, source: functionSource }
845
866
  };
846
867
  };
@@ -849,7 +870,7 @@ const isMultiPartUploadPath = (path) => {
849
870
  const [, entity, ...rest] = path.split('/');
850
871
  return entity && rest.length === 2 && rest[1] === 'multipartUpload';
851
872
  };
852
- const groupEndpointsByEntity = (paths) => {
873
+ const parseEndpointsAndGroupByEntity = (paths) => {
853
874
  const endpoints = new Map();
854
875
  for (const [rawPath, path] of Object.entries(paths)) {
855
876
  const endpoint = parseEndpointPath(rawPath);
@@ -861,11 +882,11 @@ const groupEndpointsByEntity = (paths) => {
861
882
  logger.errorLn(`Failed to parse ${rawPath}`);
862
883
  continue;
863
884
  }
864
- if (endpoints.has(endpoint.entity)) {
865
- endpoints.get(endpoint.entity)?.push({ endpoint, path });
885
+ if (endpoints.has(endpoint.service)) {
886
+ endpoints.get(endpoint.service)?.push({ endpoint, path });
866
887
  }
867
888
  else {
868
- endpoints.set(endpoint.entity, [{ endpoint, path }]);
889
+ endpoints.set(endpoint.service, [{ endpoint, path }]);
869
890
  }
870
891
  }
871
892
  return endpoints;
@@ -898,26 +919,34 @@ const generators = {
898
919
  post: generateGenericEndpoint()
899
920
  }
900
921
  };
901
- const generateServices = (doc, aliases, options) => {
922
+ const generateServices = (paths, entities, aliases, options) => {
902
923
  const services = new Map();
903
- const grouped = groupEndpointsByEntity(doc.paths);
904
- for (const [endpoint, paths] of grouped) {
905
- const serviceName = camelCase(`${endpoint}Service`);
906
- const serviceTypeName = pascalCase(`${endpoint}Service`);
907
- // Service functions
924
+ const endpoints = parseEndpointsAndGroupByEntity(paths);
925
+ for (const [serviceName, paths] of endpoints) {
926
+ const serviceFnName = camelCase(`${serviceName}Service`);
927
+ const serviceTypeName = pascalCase(`${serviceName}Service`);
908
928
  const functions = [];
909
929
  for (const { path, endpoint } of paths) {
910
- const resolver = generators[endpoint.type];
930
+ const generator = generators[endpoint.type];
911
931
  for (const [method, config] of Object.entries(path)) {
912
932
  if (method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique) {
933
+ // Skip unique endpoints if generateUnique option is not set
913
934
  continue;
914
935
  }
915
- if (resolver[method]) {
936
+ const generatorFn = generator[method];
937
+ if (generatorFn) {
916
938
  const path = config;
917
939
  const target = options.target;
918
940
  if (!path.deprecated || options.deprecated) {
919
941
  functions.push({
920
- ...resolver[method]({ endpoint, method, target, path, aliases }),
942
+ ...generatorFn({
943
+ endpoint,
944
+ method,
945
+ target,
946
+ path,
947
+ entities,
948
+ aliases
949
+ }),
921
950
  path
922
951
  });
923
952
  }
@@ -930,33 +959,28 @@ const generateServices = (doc, aliases, options) => {
930
959
  if (!functions.length) {
931
960
  continue;
932
961
  }
933
- // Construct service type
934
- const types = generateStatements(...functions.flatMap((v) => v.interfaces?.map((v) => v.source) ?? []), ...functions.map((v) => v.type.source), generateInterface(serviceTypeName, [
962
+ 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, [
935
963
  ...functions.map((v) => ({
936
964
  required: true,
937
965
  comment: v.path.deprecated ? '@deprecated' : undefined,
938
966
  name: v.func.name,
939
967
  type: v.type.name
940
968
  }))
941
- ]));
942
- // Construct service value
943
- const funcBody = generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`);
944
- const func = `export const ${serviceName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${funcBody};`;
945
- const source = generateBlockComment(`${pascalCase(endpoint)} service`, generateStatements(types, func));
946
- const deprecated = functions.every((v) => v.path.deprecated);
947
- services.set(endpoint, {
948
- entity: endpoint,
949
- deprecated,
950
- serviceName,
969
+ ])));
970
+ const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
971
+ services.set(serviceName, {
972
+ name: serviceName,
973
+ serviceFnName,
951
974
  serviceTypeName,
952
- source,
953
- functions
975
+ functions,
976
+ source: generateStatements(serviceTypes, serviceFn),
977
+ deprecated: functions.every((v) => v.path.deprecated)
954
978
  });
955
979
  }
956
980
  return services;
957
981
  };
958
982
 
959
- const generateCustomValueUtilities = (entities, services) => {
983
+ const generateCustomValueServices = (entities, services) => {
960
984
  const customValueEntity = entities.get('customValue');
961
985
  const customValueEntities = [];
962
986
  if (!customValueEntity) {
@@ -977,32 +1001,32 @@ const generateCustomValueUtilities = (entities, services) => {
977
1001
  continue serviceLoop;
978
1002
  }
979
1003
  }
980
- customValueEntities.push(service.entity);
1004
+ customValueEntities.push(service.name);
981
1005
  }
982
- 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);')}`));
1006
+ 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);')}`);
983
1007
  };
984
1008
 
985
1009
  const generateObject = (properties) => {
986
1010
  const body = [];
987
- for (const { key, value } of properties) {
1011
+ for (const { key, value, comment } of properties) {
988
1012
  if (value === undefined) {
989
1013
  continue;
990
1014
  }
991
1015
  if (Array.isArray(value)) {
992
1016
  const str = generateObject(value);
993
1017
  if (str.length > 2) {
994
- body.push(`${key}: ${str}`);
1018
+ body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${str}`);
995
1019
  }
996
1020
  }
997
1021
  else {
998
- body.push(`${key}: ${String(value)}`);
1022
+ body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${String(value)}`);
999
1023
  }
1000
1024
  }
1001
1025
  return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
1002
1026
  };
1003
1027
 
1004
1028
  const resolveInheritedEntities = (root, entities) => {
1005
- const parent = root.extends ? entities.get(root.extends) : undefined;
1029
+ const parent = root.parentName ? entities.get(root.parentName) : undefined;
1006
1030
  return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
1007
1031
  };
1008
1032
  const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
@@ -1012,7 +1036,7 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [..
1012
1036
  return true;
1013
1037
  }
1014
1038
  // Check if corresponding service is deprecated and can be removed
1015
- const service = services.find((v) => v.entity === meta.service);
1039
+ const service = services.find((v) => v.name === meta.service);
1016
1040
  return !meta.service || (service && !service.deprecated);
1017
1041
  })
1018
1042
  .map(([property, meta]) => ({
@@ -1022,9 +1046,12 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [..
1022
1046
  value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
1023
1047
  }))
1024
1048
  }));
1025
- const generateEntityPropertyMap = (entities, services, options) => {
1049
+ const generateEntityProperties = (entities, aliases, services, options) => {
1026
1050
  const typeName = 'WEntityProperties';
1027
- const propertyMap = [...entities].map(([entity, data]) => ({
1051
+ const propertyMap = [
1052
+ ...entities.entries(),
1053
+ ...aliases.entries().map(([service, type]) => [service, entities.get(camelCase(type))])
1054
+ ].map(([entity, data]) => ({
1028
1055
  key: entity,
1029
1056
  value: generatePropertyDescriptors(data, entities, services, options)
1030
1057
  }));
@@ -1045,17 +1072,17 @@ const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
1045
1072
  */
1046
1073
  const generateGroupedServices = (services) => {
1047
1074
  const entityDescriptors = new Map();
1048
- for (const { entity, functions } of services) {
1049
- for (const { name } of functions) {
1050
- if (!FILTER_REGEX.test(name)) {
1075
+ for (const service of services) {
1076
+ for (const fn of service.functions) {
1077
+ if (!FILTER_REGEX.test(fn.name)) {
1051
1078
  continue;
1052
1079
  }
1053
- entityDescriptors.set(name, [
1054
- ...(entityDescriptors.get(name) ?? []),
1080
+ entityDescriptors.set(fn.name, [
1081
+ ...(entityDescriptors.get(fn.name) ?? []),
1055
1082
  {
1056
- name: entity,
1083
+ name: service.name,
1057
1084
  required: true,
1058
- type: `${pascalCase(entity)}Service_${pascalCase(name)}`
1085
+ type: `${pascalCase(service.name)}Service_${pascalCase(fn.name)}`
1059
1086
  }
1060
1087
  ]);
1061
1088
  }
@@ -1068,131 +1095,92 @@ const generateGroupedServices = (services) => {
1068
1095
  const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
1069
1096
  typeGuards.push(`export const is${service} = ${guard}`);
1070
1097
  }
1071
- return [
1072
- ...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)),
1073
- ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)),
1074
- ...descriptors.map(([name, props]) => {
1075
- const constant = camelCase(`wServiceWith_${name}_Names`);
1076
- const type = pascalCase(`WServiceWith_${name}`);
1077
- const value = generateArray(props.map((v) => v.name));
1078
- return `export const ${constant}: ${type}[] = ${value};`;
1079
- }),
1080
- generateBlockComment('Type guards for service classes.', generateStatements(...typeGuards))
1081
- ];
1098
+ 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]) => {
1099
+ const constant = camelCase(`wServiceWith_${name}_Names`);
1100
+ const type = pascalCase(`WServiceWith_${name}`);
1101
+ const value = generateArray(props.map((v) => v.name));
1102
+ return `export const ${constant}: ${type}[] = ${value};`;
1103
+ }), ...typeGuards);
1082
1104
  };
1083
1105
 
1084
- const obj = (list) => `{\n${indent(list.join(',\n'))}\n}`;
1085
- const arr = (list) => `[\n${indent(list.join(',\n'))}\n]`;
1086
- const generateMaps = ({ services, entities, aliases, enums, options }) => {
1087
- const entitiesKeys = [...entities.keys()];
1088
- const enumsArray = `export const wEnums = ${obj(enums)};`;
1089
- const entityNames = `export const wEntityNames: WEntity[] = ${arr(entitiesKeys.map((v) => `'${v}'`))};`;
1090
- const serviceNames = `export const wServiceNames: WService[] = ${arr(services.map((v) => `'${v.entity}'`))};`;
1091
- const serviceValues = `export const wServiceFactories = ${obj(services.map((v) => `${v.entity}: ${v.serviceName}`))};`;
1092
- const serviceInstanceValues = `export const wServices = ${obj(services.map((v) => {
1093
- const src = `${v.entity}: ${v.serviceName}()`;
1094
- return v.deprecated ? generateInlineComment('@deprecated') + `\n${src}` : src;
1095
- }))};`;
1096
- const entityInterfaces = [
1097
- ...entitiesKeys.map((entity) => ({
1098
- name: entity,
1099
- type: pascalCase(entity),
1100
- required: true
1101
- })),
1102
- ...services.map((service) => {
1103
- const alias = aliases.get(service.entity);
1104
- return {
1105
- name: service.entity,
1106
- type: alias ?? 'never',
1107
- required: true,
1108
- comment: alias ? undefined : 'no response defined or inlined'
1109
- };
1110
- })
1111
- ];
1112
- const createMappingType = (type, prefix) => (type !== 'never' ? `${type}_${prefix}` : type);
1113
- const entitiesList = generateInterface('WEntities', entityInterfaces);
1114
- const entityReferences = generateInterface('WEntityReferences', entityInterfaces.map((v) => ({
1115
- ...v,
1116
- type: createMappingType(v.type, 'References')
1117
- })));
1118
- const entityMappings = generateInterface('WEntityMappings', entityInterfaces.map((v) => ({
1119
- ...v,
1120
- type: createMappingType(v.type, 'Mappings')
1121
- })));
1122
- const entityFilter = generateInterface('WEntityFilters', entityInterfaces.map((v) => ({
1123
- ...v,
1124
- type: createMappingType(v.type, 'Filter')
1125
- })));
1106
+ const generateMaps = (enums, entities, services, aliases, options) => {
1107
+ const enumInstances = `export const wEnums = ${generateObject(enums
1108
+ .keys()
1109
+ .toArray()
1110
+ .map((v) => ({ key: v, value: v })))};`;
1111
+ const entityNames = `export const wEntityNames: WEntity[] = ${generateArray(entities.keys().toArray())};`;
1112
+ const generatedServices = services.values().toArray();
1113
+ const serviceInstances = `export const wServices = ${generateObject(generatedServices.map((v) => ({
1114
+ key: v.name,
1115
+ value: `${v.serviceFnName}()`,
1116
+ comment: v.deprecated ? '@deprecated' : undefined
1117
+ })))};`;
1118
+ const serviceFactories = `export const wServiceFactories = ${generateObject(generatedServices.map((v) => ({
1119
+ key: v.name,
1120
+ value: v.serviceFnName,
1121
+ comment: v.deprecated ? '@deprecated' : undefined
1122
+ })))};`;
1126
1123
  return {
1127
1124
  source: generateStatements(
1128
- /* JS Values */
1129
- serviceValues, serviceInstanceValues, entityNames, serviceNames, enumsArray, generateEntityPropertyMap(entities, services, options),
1130
- /* Map of entity to references / mappings and filters*/
1131
- entityReferences, entityMappings, entityFilter,
1132
- /* List of all entities with their corresponding service */
1133
- generateBlockComment(`
1134
- This interfaces merges two maps:
1135
- - Map<[entityName], [entityInterfaceName]>
1136
- - Map<[serviceName], [entityInterfaceName]>
1137
-
1138
- Where [entityName] is
1139
- - the name of a nested entity (e.g. 'address' from Party)
1140
- - the name of an entity (e.g. 'party', 'article' etc.)
1141
-
1142
- Where [serviceName] is the name of an endpoint (e.g. for /article its 'article')
1143
-
1144
- Where [entityInterfaceName] is
1145
- - the underlying type for this entity
1146
- - the type for what is returned by the api
1147
- `, entitiesList),
1148
- /* type-ofs and types */
1149
- generateType('WServices', 'typeof wServices'), generateType('WServiceFactories', 'typeof wServiceFactories'), generateType('WService', 'keyof WServices'), generateType('WEntity', 'keyof WEntities'), generateType('WEnums', 'typeof wEnums'), generateType('WEnum', 'keyof WEnums'),
1150
- /* Utilities. */
1151
- generateCustomValueUtilities(entities, services),
1152
- /* All functions grouped by service supporting it */
1153
- ...generateGroupedServices(services))
1125
+ /* Enums */
1126
+ generateInterface('WEnums', enums
1127
+ .keys()
1128
+ .toArray()
1129
+ .map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
1130
+ /* Entities */
1131
+ generateInterface('WEntities', [
1132
+ ...entities.keys().map((name) => ({ name, type: loosePascalCase(name), required: true })),
1133
+ ...aliases.entries().map(([name, type]) => ({ name, type, required: true }))
1134
+ ].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
1135
+ /* Services */
1136
+ serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
1137
+ /* Service Utils */
1138
+ generateGroupedServices(generatedServices), generateCustomValueServices(entities, generatedServices),
1139
+ /* Entity Properties (Runtime Meta Infos) */
1140
+ generateEntityProperties(entities, aliases, generatedServices, options))
1154
1141
  };
1155
1142
  };
1156
1143
 
1157
- const parseReferencedEntity = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
1158
- const extractSchemas = (doc) => {
1159
- const schemas = new Map();
1144
+ const parseReferencedEntityType = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
1145
+ const extractServiceAliases = (doc, schemas) => {
1160
1146
  const aliases = new Map();
1161
- for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
1162
- if (!isReferenceObject(schema)) {
1163
- schemas.set(name, schema);
1164
- }
1165
- }
1166
- /**
1167
- * Referenced schemas in responses, in some case the response from the root endpoint
1168
- * refers to a schema with a different name
1169
- */
1170
1147
  for (const [path, methods] of Object.entries(doc.paths)) {
1171
1148
  const parsed = parseEndpointPath(path);
1172
- if (!parsed || schemas.has(parsed.entity)) {
1149
+ if (!parsed || !methods || parsed.type !== WeclappEndpointType.ROOT || schemas.has(parsed.service)) {
1173
1150
  continue;
1174
1151
  }
1175
- for (const method of Object.values(OpenAPIV3.HttpMethods)) {
1176
- const body = methods[method]?.responses['200'];
1177
- if (isResponseObject(body) && body.content) {
1178
- const responseSchema = Object.values(body.content)[0]?.schema;
1179
- if (isReferenceObject(responseSchema)) {
1180
- continue;
1181
- }
1182
- const itemsSchema = responseSchema?.properties?.result;
1183
- if (isReferenceObject(itemsSchema)) {
1184
- aliases.set(parsed.entity, parseReferencedEntity(itemsSchema));
1185
- continue;
1186
- }
1187
- if (isArraySchemaObject(itemsSchema)) {
1188
- const { items } = itemsSchema;
1189
- if (isReferenceObject(items)) {
1190
- aliases.set(parsed.entity, parseReferencedEntity(items));
1191
- }
1152
+ const body = methods[OpenAPIV3.HttpMethods.GET]?.responses['200'];
1153
+ if (isResponseObject(body) && body.content?.['application/json']) {
1154
+ const responseSchema = body.content['application/json'].schema;
1155
+ if (!responseSchema || isReferenceObject(responseSchema)) {
1156
+ continue;
1157
+ }
1158
+ const resultSchema = responseSchema.properties?.result;
1159
+ if (!resultSchema) {
1160
+ continue;
1161
+ }
1162
+ if (isReferenceObject(resultSchema)) {
1163
+ aliases.set(parsed.service, parseReferencedEntityType(resultSchema));
1164
+ continue;
1165
+ }
1166
+ if (isArraySchemaObject(resultSchema)) {
1167
+ const resultItemSchema = resultSchema.items;
1168
+ if (isReferenceObject(resultItemSchema)) {
1169
+ aliases.set(parsed.service, parseReferencedEntityType(resultItemSchema));
1192
1170
  }
1193
1171
  }
1194
1172
  }
1195
1173
  }
1174
+ return aliases;
1175
+ };
1176
+ const extractSchemas = (doc) => {
1177
+ const schemas = new Map();
1178
+ for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
1179
+ if (!isReferenceObject(schema)) {
1180
+ schemas.set(name, schema);
1181
+ }
1182
+ }
1183
+ const aliases = extractServiceAliases(doc, schemas);
1196
1184
  return { schemas, aliases };
1197
1185
  };
1198
1186
 
@@ -1200,14 +1188,18 @@ const generate = (doc, options) => {
1200
1188
  const { schemas, aliases } = extractSchemas(doc);
1201
1189
  const enums = generateEnums(schemas);
1202
1190
  const entities = generateEntities(schemas, enums);
1203
- const services = generateServices(doc, aliases, options);
1204
- return generateStatements(generateBase(options.target, doc.info.version, options.useQueryLanguage), 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({
1205
- services: [...services.values()],
1206
- enums: [...enums.keys()],
1207
- options,
1208
- entities,
1209
- aliases
1210
- }).source));
1191
+ const services = generateServices(doc.paths, entities, aliases, options);
1192
+ const maps = generateMaps(enums, entities, services, aliases, options);
1193
+ return generateStatements(generateBase(options.target, doc.info.version, options), generateBlockComment('ENUMS', generateStatements(...enums
1194
+ .values()
1195
+ .map((v) => v.source)
1196
+ .toArray())), generateBlockComment('ENTITIES', generateStatements(...entities
1197
+ .values()
1198
+ .map((v) => v.source)
1199
+ .toArray())), generateBlockComment('SERVICES', generateStatements(...services
1200
+ .values()
1201
+ .map((v) => v.source)
1202
+ .toArray())), generateBlockComment('MAPS', generateStatements(maps.source)));
1211
1203
  };
1212
1204
 
1213
1205
  const hash = (content, algorithm = 'sha256') => {
@@ -1229,6 +1221,7 @@ const cli = async () => {
1229
1221
  .usage('Usage: $0 <source> [flags]')
1230
1222
  .version(version)
1231
1223
  .example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
1224
+ .example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
1232
1225
  .example('$0 xxx.weclapp.com --key ...', 'Generate the SDK based on the openapi file from the given weclapp instance')
1233
1226
  .help('h')
1234
1227
  .alias('v', 'version')
@@ -1356,7 +1349,7 @@ void (async () => {
1356
1349
  logger.infoLn('Bundle... (this may take some time)');
1357
1350
  await bundle(workingDir, options.target);
1358
1351
  // Remove index.ts (only bundle is required)
1359
- await rm(await workingDirPath('src'), { recursive: true, force: true });
1352
+ // await rm(await workingDirPath('src'), { recursive: true, force: true });
1360
1353
  if (useCache) {
1361
1354
  // Copy SDK to cache
1362
1355
  logger.successLn(`Caching SDK: (${cachedSdkDir})`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weclapp/sdk",
3
- "version": "2.0.0-dev.30",
3
+ "version": "2.0.0-dev.31",
4
4
  "description": "SDK generator based on a weclapp api swagger file",
5
5
  "author": "weclapp",
6
6
  "sideEffects": false,