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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,17 +1,18 @@
1
1
  import { fileURLToPath } from 'url';
2
2
  import { resolve, dirname } from 'path';
3
- import { rmdir, stat, readFile, writeFile, rm, cp, mkdir } from 'fs/promises';
4
3
  import { rollup } from 'rollup';
5
4
  import terser from '@rollup/plugin-terser';
6
- import ts from 'rollup-plugin-ts';
5
+ import ts from '@rollup/plugin-typescript';
7
6
  import indentString from 'indent-string';
8
7
  import { snakeCase, pascalCase, camelCase } from 'change-case';
9
8
  import chalk from 'chalk';
10
9
  import { OpenAPIV3 } from 'openapi-types';
11
10
  import { createHash } from 'crypto';
11
+ import { stat, readFile, rm, cp, writeFile, mkdir } from 'fs/promises';
12
12
  import { config } from 'dotenv';
13
13
  import yargs from 'yargs';
14
14
  import { hideBin } from 'yargs/helpers';
15
+ import pkg from '../package.json' with { type: 'json' };
15
16
  import prettyMs from 'pretty-ms';
16
17
 
17
18
  var Target;
@@ -39,8 +40,8 @@ const currentDirname = () => {
39
40
  return fileURLToPath(new URL('..', import.meta.url));
40
41
  };
41
42
 
42
- const tsconfig = resolve(currentDirname(), './tsconfig.lib.json');
43
- const resolveGlobals = (...globals) => Object.fromEntries(globals.map(v => [v, '*']));
43
+ const tsconfig = resolve(currentDirname(), './tsconfig.sdk.json');
44
+ const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, '*']));
44
45
  const generateOutput = (config) => ({
45
46
  sourcemap: true,
46
47
  banner: `/* weclapp sdk */`,
@@ -61,35 +62,22 @@ const bundle = async (workingDirectory, target) => {
61
62
  globals: resolveGlobals('node-fetch', 'url')
62
63
  })
63
64
  ];
64
- // Remove build dir
65
- await rmdir(dist()).catch(() => void 0);
66
65
  const bundles = {
67
66
  [Target.BROWSER_PROMISES]: () => ({
68
- input: src('browser.ts'),
67
+ input: src('index.ts'),
68
+ plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
69
69
  output: [
70
- generateOutput({
71
- file: dist('index.cjs'),
72
- name: 'Weclapp',
73
- format: 'umd'
74
- }),
75
70
  generateOutput({
76
71
  file: dist('index.js'),
77
72
  format: 'es'
78
73
  })
79
- ],
80
- plugins: [ts({ tsconfig }), terser()]
74
+ ]
81
75
  }),
82
76
  [Target.BROWSER_RX]: () => ({
77
+ input: src('index.ts'),
78
+ plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
83
79
  external: ['rxjs'],
84
- input: src('browser.rx.ts'),
85
- plugins: [ts({ tsconfig }), terser()],
86
80
  output: [
87
- generateOutput({
88
- file: dist('index.cjs'),
89
- name: 'Weclapp',
90
- format: 'umd',
91
- globals: resolveGlobals('rxjs')
92
- }),
93
81
  generateOutput({
94
82
  file: dist('index.js'),
95
83
  format: 'es',
@@ -98,16 +86,16 @@ const bundle = async (workingDirectory, target) => {
98
86
  ]
99
87
  }),
100
88
  [Target.NODE_PROMISES]: () => ({
101
- input: src('node.ts'),
102
- output: generateNodeOutput(),
89
+ input: src('index.ts'),
90
+ plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
103
91
  external: ['node-fetch', 'url'],
104
- plugins: [ts({ tsconfig })]
92
+ output: generateNodeOutput()
105
93
  }),
106
94
  [Target.NODE_RX]: () => ({
107
- input: src('node.rx.ts'),
108
- output: generateNodeOutput(),
95
+ input: src('index.ts'),
96
+ plugins: [ts({ tsconfig, declarationDir: dist(), filterRoot: src() }), terser()],
109
97
  external: ['node-fetch', 'url', 'rxjs'],
110
- plugins: [ts({ tsconfig })]
98
+ output: generateNodeOutput()
111
99
  })
112
100
  };
113
101
  const config = bundles[target]();
@@ -139,14 +127,22 @@ const indent = (s, level = 1) => {
139
127
  };
140
128
 
141
129
  const generateStatements = (...statements) => statements
142
- .map(v => v.trim())
143
- .filter(v => v.length)
130
+ .map((v) => v.trim())
131
+ .filter((v) => v.length)
144
132
  .join('\n\n');
145
133
  const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
146
134
 
147
- var root = "export type RequestPayloadMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';\n\nexport interface RequestPayload {\n method?: RequestPayloadMethod;\n query?: Record<string, any>;\n body?: any;\n unwrap?: boolean;\n forceBlob?: boolean;\n}\n\nexport interface ServiceConfig {\n\n // Your API-Key, this is optional in the sense of if you omit this, and you're in a browser, the\n // cookie-authentication (include-credentials) will be used.\n key?: string;\n\n // Your domain, if omitted location.host will be used.\n host?: string;\n\n // If you want to use https, defaults to true.\n secure?: boolean;\n\n // Optional request/response interceptors.\n interceptors?: {\n\n // Takes the generated request, you can either return a new request,\n // a response (which will be taken as \"the\" response) or nothing.\n // The payload contains the raw input generated by the SDK.\n request?: (request: Request, payload: RequestPayload) => Request | Response | void | Promise<Request | Response | void>;\n\n // Takes the response. This can either be the one from the server or an\n // artificially-crafted one by the request interceptor.\n response?: (response: Response) => Response | void | Promise<Response | void>;\n };\n}\n\nlet globalConfig: ServiceConfig | undefined;\nexport const getGlobalConfig = (): ServiceConfig | undefined => globalConfig;\nexport const setGlobalConfig = (cfg?: ServiceConfig) => globalConfig = cfg;\n\nexport const raw = async (\n cfg: ServiceConfig | undefined = globalConfig,\n endpoint: string,\n payload: RequestPayload = {}\n): Promise<any> => {\n if (!cfg) {\n throw new Error(`ServiceConfig missing.`);\n }\n\n cfg = {\n ...globalConfig, ...cfg,\n interceptors: {...globalConfig?.interceptors, ...cfg?.interceptors}\n };\n\n const isBinaryData = payload.body instanceof resolveBinaryObject();\n const params = new URLSearchParams(Object.entries(payload.query ?? {}).filter(v => v[1] !== undefined));\n const protocol = (cfg.secure ?? true) ? 'https' : 'http';\n\n const interceptRequest = cfg.interceptors?.request ?? (v => v);\n const interceptResponse = cfg.interceptors?.response ?? (v => v);\n\n let host = cfg.host?.replace(/^https?:\\/\\//, '');\n if (!host && typeof location !== 'undefined') {\n host = location.host;\n }\n\n if (!host) {\n throw new Error('Please specify a domain');\n }\n\n const request = new Request(`${protocol}://${host}/webapp/api/v1${endpoint}?${params}`, {\n ...(payload.body && {\n body: isBinaryData\n ? payload.body\n : JSON.stringify(payload.body, (key, value) => value === undefined ? null : value)\n }),\n ...(!cfg.key && {credentials: 'same-origin'}),\n method: payload.method ?? 'get',\n headers: {\n 'Accept': 'application/json',\n ...(cfg.key && {'AuthenticationToken': cfg.key}),\n ...(!isBinaryData && {'Content-Type': 'application/json'})\n }\n });\n\n let res = await interceptRequest(request, payload) ?? request;\n if (!(res instanceof Response)) {\n res = await fetch(res);\n }\n\n res = await interceptResponse(res) ?? res;\n const data = (!payload.forceBlob || !res.ok) && res.headers?.get('content-type')?.includes('application/json') ?\n await res.json() : await res.blob();\n\n // Check if response was successful\n if (!res.ok) {\n return Promise.reject(data);\n }\n\n return payload.unwrap ? data.result : data;\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & {params?: Record<any, any>}\n) => wrapResponse(() => raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\n }\n}));\n\nconst _some = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: SomeQuery<any, any, any, any, any> & {params?: Record<any, any>}\n) => wrapResponse(() => 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 _remove = (\n cfg: ServiceConfig | undefined,\n endpoint: string\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'DELETE'\n}).then(() => undefined));\n\nconst _create = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n data: any\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'POST',\n body: data\n}));\n\nconst _update = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n data: any,\n {ignoreMissingProperties = true}: UpdateQuery = {}\n) => wrapResponse(() => raw(cfg, endpoint, {\n method: 'PUT',\n body: data,\n query: {ignoreMissingProperties}\n}));\n\nconst _unique = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: UniqueQuery\n) => wrapResponse(() => raw(cfg, endpoint, {query}));\n\nconst _generic = (\n cfg: ServiceConfig | undefined,\n method: RequestPayloadMethod,\n endpoint: string,\n payload?: GenericQuery<any, any>,\n forceBlob?: boolean\n) => wrapResponse(() => raw(cfg, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n}));\n";
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
+
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";
138
+
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";
140
+
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";
142
+
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";
148
144
 
149
- var types = "export type DeepPartial<T> = T extends object ? {\n [P in keyof T]?: DeepPartial<T[P]>;\n} : T;\n\n// Filter properties\nexport type EqualityOperators = 'EQ' | 'NE';\nexport type ComparisonOperators = 'LT' | 'GT' | 'LE' | 'GE' | 'LIKE' | 'ILIKE' | 'NOT_LIKE' | 'NOT_ILIKE';\nexport type ArrayOperators = 'IN' | 'NOT_IN';\nexport type Operator = EqualityOperators | ComparisonOperators | ArrayOperators;\n\nexport type MapOperators<T> = { [K in EqualityOperators]?: T | null; } &\n { [K in ComparisonOperators]?: T; } &\n { [K in ArrayOperators]?: T[]; };\n\nexport type QueryFilter<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ?\n U extends Record<any, any> ? QueryFilter<U> : MapOperators<U> :\n T[P] extends Record<any, any> | undefined ? QueryFilter<T[P]> : MapOperators<T[P]>;\n};\n\nexport type Sort<T> = {\n [K in keyof T]?: {\n [V in keyof T]?:\n V extends K ?\n T[V] extends Array<infer U> | undefined ?\n U extends object ?\n Sort<U> : never :\n T[V] extends object | undefined ?\n Sort<T[V]> : 'asc' | 'desc' : never;\n };\n}[keyof T];\n\n// Select properties\nexport type CustomAttributeFilter = {\n [K in number]: string | number | boolean |\n {id: string;} |\n {entityName: string; entityId: string;};\n}\n\nexport type QuerySelect<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ? (QuerySelect<U> | boolean) :\n T[P] extends Record<any, any> | undefined ? (QuerySelect<T[P]> | boolean) : boolean;\n}\n\nexport type Select<T, Q extends (QuerySelect<T> | undefined)> = Q extends QuerySelect<T> ? {\n\n // Filter out excluded properties beforehand\n [P in keyof T as Q[P] extends boolean ? P : Q[P] extends object ? P : never]:\n\n // Property\n Q[P] extends true ? T[P] :\n\n // Array\n T[P] extends Array<infer U> ? Select<U, Q[P] & QuerySelect<any>>[] :\n\n // Object\n T[P] extends Record<any, any> ? Select<T[P], Q[P] & QuerySelect<any>> : never\n} : undefined;\n\nexport type MapKeys<T, S extends Record<keyof T, string>> = {\n [K in keyof T as S[K]]: T[K];\n};\n\n// Endpoint configurations\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n};\n\nexport type Pagination = {\n page: number;\n pageSize: number;\n};\n\nexport type SomeQuery<\n E, // Entity\n F, // Entity filter\n I extends (QuerySelect<any> | undefined), // Select for referenced entities\n S extends (QuerySelect<any> | undefined), // Select for entity properties\n 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\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}\n\n// Entity meta types\nexport type WEntityPropertyMeta = (\n { type: 'string'; format?: 'decimal' | 'html'; entity?: WEntity; service?: WService; } |\n { type: 'integer'; format: 'int32' | 'int64' | 'duration' | 'date' | 'timestamp'; } |\n { type: 'array'; format: 'reference'; entity: WEntity; service?: WService; } |\n { type: 'array'; format: 'reference'; enum: WEnum; } |\n { type: 'array'; format: 'string'; } |\n { type: 'number'; format: 'double'; } |\n { type: 'reference'; entity: WEntity; } |\n { type: 'reference'; enum: WEnum; } |\n { type: 'boolean'; } |\n { type: 'object'; }\n);\n\n// Utils\nconst equality: string[] = ['EQ', 'NE'];\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\nconst array: string[] = ['IN', 'NOT_IN'];\nconst filterMap: Record<Operator, string> = {\n EQ: 'eq',\n NE: 'ne',\n LT: 'lt',\n GT: 'gt',\n LE: 'le',\n GE: 'ge',\n LIKE: 'like',\n NOT_LIKE: 'notlike',\n ILIKE: 'ilike',\n NOT_ILIKE: 'notilike',\n IN: 'in',\n NOT_IN: 'notin'\n};\n\nconst flattenCustomAttributes = (obj: CustomAttributeFilter = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [id, filter] of Object.entries(obj)) {\n const key = `customAttribute${id}`;\n\n if (typeof filter === 'object') {\n for (const [prop, value] of Object.entries(filter)) {\n entries.push([`${key}.${prop}-eq`, String(value)]);\n }\n } else if (filter !== undefined) {\n entries.push([`${key}-eq`, String(filter)]);\n }\n }\n\n return entries;\n};\n\nconst flatten = (obj: QueryFilter<any> = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [prop, propValue] of Object.entries(obj)) {\n for (const [filter, value] of Object.entries(propValue as object)) {\n if (value === undefined) continue;\n\n if (simple.includes(filter)) {\n if (value === null && equality.includes(filter)) {\n entries.push([`${prop}-${filter === 'EQ' ? 'null' : 'notnull'}`, '']);\n } else {\n entries.push([`${prop}-${filterMap[filter as Operator]}`, value]);\n }\n } else if (array.includes(filter)) {\n entries.push([\n `${prop}-${filterMap[filter as Operator]}`,\n `[${value.map((v: string | number) => typeof v === 'string' ? `\"${v}\"` : v)}]`\n ]);\n } else {\n entries.push(\n ...flatten(propValue as QueryFilter<any>)\n .map(v => [`${prop}.${v[0]}`, v[1]]) as [string, string][]\n );\n break;\n }\n }\n }\n\n return entries;\n};\n\nconst flattenFilter = (obj: QueryFilter<any> = {}): Record<string, string> => {\n const filter: [string, any][] = [], customAttributes: [string, any][] = [];\n\n Object.entries(obj).forEach(value => {\n (value[0].match(/^\\d+$/) ? customAttributes : filter).push(value);\n });\n\n return Object.fromEntries([\n ...flatten(Object.fromEntries(filter)),\n ...flattenCustomAttributes(Object.fromEntries(customAttributes) as CustomAttributeFilter)\n ]);\n};\n\nconst flattenOrFilter = (obj: QueryFilter<any>[] = []): Record<string, string> => {\n const entries: [string, any][] = [];\n\n for (let i = 0; i < obj.length; i++) {\n entries.push(\n ...flatten(obj[i])\n .map(v => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]\n );\n }\n\n return Object.fromEntries(entries);\n};\n\nconst flattenSelect = (obj: Select<any, any> = {}): string[] => {\n const entries: string[] = [];\n\n for (const [prop, value] of Object.entries(obj)) {\n if (typeof value === 'object' && value) {\n entries.push(...flattenSelect(value).map(v => `${prop}.${v}`));\n } else if (value) {\n entries.push(prop);\n }\n }\n\n return entries;\n};\n\nexport const flattenSort = (obj: Sort<any>[] = []): {sort?: string} => {\n const flatten = (obj: Sort<any>, base = ''): string | undefined => {\n const [key, value] = Object.entries(obj ?? {})[0] ?? [];\n\n if (key && value) {\n const path = base + key;\n\n if (typeof value === 'object') {\n return flatten(value, path ? `${path}.` : '');\n } else if (['asc', 'desc'].includes(value)) {\n return `${value === 'desc' ? '-' : ''}${path}`;\n }\n }\n\n return undefined;\n };\n\n const sorts = obj.map(v => flatten(v)).filter(Boolean);\n return sorts.length ? {sort: sorts.join(',')} : {};\n};\n";
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";
150
146
 
151
147
  const resolveImports = (target) => {
152
148
  const imports = [];
@@ -157,13 +153,13 @@ const resolveImports = (target) => {
157
153
  };
158
154
  const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
159
155
  const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
160
- const generateBase = (target) => {
161
- return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), types, root);
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);
162
158
  };
163
159
 
164
160
  const transformKey = (s) => snakeCase(s).toUpperCase();
165
161
  const generateEnum = (name, values) => {
166
- const props = indent(values.map(v => `${transformKey(v)} = ${generateString(v)}`).join(',\n'));
162
+ const props = indent(values.map((v) => `${transformKey(v)} = ${generateString(v)}`).join(',\n'));
167
163
  return `export enum ${name} {\n${props}\n}`;
168
164
  };
169
165
 
@@ -218,16 +214,13 @@ const concat = (strings, separator = ', ', maxLength = 80) => {
218
214
  const joined = strings.join(separator);
219
215
  if (joined.length > maxLength) {
220
216
  const length = strings.length - 1;
221
- return `\n${indent(strings
222
- .map((value, index) => index === length ? value : `${(value + separator).trim()}\n`)
223
- .join(''))}\n`;
217
+ return `\n${indent(strings.map((value, index) => (index === length ? value : `${(value + separator).trim()}\n`)).join(''))}\n`;
224
218
  }
225
219
  else {
226
220
  return joined;
227
221
  }
228
222
  };
229
223
 
230
- /* eslint-disable no-use-before-define */
231
224
  const createReferenceType = (value) => ({
232
225
  type: 'reference',
233
226
  toString: () => loosePascalCase(value)
@@ -238,26 +231,28 @@ const createRawType = (value) => ({
238
231
  });
239
232
  const createArrayType = (value) => ({
240
233
  type: 'array',
241
- toString: () => `${value.toString()}[]`
234
+ toString: () => `(${value.toString()})[]`
242
235
  });
243
236
  const createTupleType = (value) => ({
244
237
  type: 'tuple',
245
- toString: () => concat([...new Set(value.map(v => typeof v === 'string' ? `'${v}'` : v.toString()))], ' | ')
238
+ toString: () => concat([...new Set(value.map((v) => (typeof v === 'string' ? `'${v}'` : v.toString())))], ' | ')
246
239
  });
247
240
  const createObjectType = (value, required = []) => ({
248
241
  type: 'object',
249
242
  isFullyOptional: () => {
250
- return !required.length && Object.values(value)
251
- .filter(v => v?.type === 'object')
252
- .every(v => v.isFullyOptional());
243
+ return (!required.length &&
244
+ Object.values(value)
245
+ .filter((v) => v?.type === 'object')
246
+ .every((v) => v.isFullyOptional()));
253
247
  },
254
248
  toString: (propagateOptionalProperties = false) => {
255
249
  const properties = Object.entries(value)
256
- .filter(v => v[1])
257
- .map(v => {
250
+ .filter((v) => v[1])
251
+ .map((v) => {
258
252
  const name = v[0];
259
253
  const value = v[1];
260
- const isRequired = required.includes(name) || (value.type === 'object' && !value.isFullyOptional() && propagateOptionalProperties);
254
+ const isRequired = required.includes(name) ||
255
+ (value.type === 'object' && !value.isFullyOptional() && propagateOptionalProperties);
261
256
  return `${name + (isRequired ? '' : '?')}: ${value.toString()};`;
262
257
  });
263
258
  return properties.length ? `{\n${indent(properties.join('\n'))}\n}` : '{}';
@@ -286,8 +281,7 @@ const convertToTypeScriptType = (schema, property) => {
286
281
  return createRawType('boolean');
287
282
  case 'object': {
288
283
  const { properties = {}, required = [] } = schema;
289
- return createObjectType(Object.fromEntries(Object.entries(properties)
290
- .map(v => [v[0], convertToTypeScriptType(v[1])])), required);
284
+ return createObjectType(Object.fromEntries(Object.entries(properties).map((v) => [v[0], convertToTypeScriptType(v[1])])), required);
291
285
  }
292
286
  case 'array':
293
287
  return createArrayType(convertToTypeScriptType(schema.items, property));
@@ -308,14 +302,19 @@ const setEntityEnumProperty = (enums, prop, meta) => {
308
302
  }
309
303
  };
310
304
  const extractPropertyMetaData = (enums, meta, prop) => {
311
- const result = { service: meta.service, entity: meta.entity };
305
+ const result = {
306
+ service: meta.service,
307
+ entity: meta.entity
308
+ };
312
309
  if (isReferenceObject(prop)) {
313
310
  setEntityEnumProperty(enums, prop, result);
314
311
  result.type = 'reference';
315
312
  return result;
316
313
  }
317
- result.format = prop.format;
318
314
  result.type = prop.type;
315
+ result.format = prop.format;
316
+ result.maxLength = prop.maxLength;
317
+ result.pattern = prop.pattern;
319
318
  if (isArraySchemaObject(prop)) {
320
319
  if (isReferenceObject(prop.items)) {
321
320
  setEntityEnumProperty(enums, prop.items, result);
@@ -335,12 +334,12 @@ const generateType = (name, value) => {
335
334
  return `export type ${name} = ${value.trim()};`;
336
335
  };
337
336
 
338
- const arrayify = (v) => Array.isArray(v) ? v : [v];
337
+ const arrayify = (v) => (Array.isArray(v) ? v : [v]);
339
338
 
340
339
  const generateInterfaceProperties = (entries) => {
341
340
  const properties = entries
342
- .filter(v => v.type !== undefined)
343
- .filter((value, index, array) => array.findIndex(v => v.name === value.name) === index)
341
+ .filter((v) => v.type !== undefined)
342
+ .filter((value, index, array) => array.findIndex((v) => v.name === value.name) === index)
344
343
  .map(({ name, type, required, readonly, comment }) => {
345
344
  const cmd = comment ? `${generateInlineComment(comment)}\n` : '';
346
345
  const req = required ? '' : '?';
@@ -384,19 +383,32 @@ const generateEntities = (schemas, enums) => {
384
383
  const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
385
384
  if (meta.entity) {
386
385
  const type = `${pascalCase(meta.entity)}[]`;
387
- referenceInterface.push({ name, type, required: true });
388
- filterInterface.push({ name: meta.entity, type, required: true });
386
+ if (schemas.has(meta.entity)) {
387
+ referenceInterface.push({ name, type, required: true });
388
+ filterInterface.push({ name: meta.entity, type, required: true });
389
+ }
389
390
  }
390
391
  if (meta.service) {
391
- referenceMappingsInterface.push({ name, type: generateString(meta.service), required: true });
392
+ if (schemas.has(meta.service)) {
393
+ referenceMappingsInterface.push({
394
+ name,
395
+ type: generateString(meta.service),
396
+ required: true
397
+ });
398
+ }
392
399
  }
393
400
  const type = convertToTypeScriptType(property, name).toString();
394
- const comment = isNonArraySchemaObject(property) ?
395
- property.deprecated ? '@deprecated will be removed.' :
396
- property.format ? `format: ${property.format}` :
397
- undefined : undefined;
401
+ const comment = isNonArraySchemaObject(property)
402
+ ? property.deprecated
403
+ ? '@deprecated will be removed.'
404
+ : property.format
405
+ ? `format: ${property.format}`
406
+ : undefined
407
+ : undefined;
398
408
  entityInterface.push({
399
- name, type, comment,
409
+ name,
410
+ type,
411
+ comment,
400
412
  required: meta.required,
401
413
  readonly: !isReferenceObject(property) && property.readOnly
402
414
  });
@@ -429,13 +441,10 @@ const generateEntities = (schemas, enums) => {
429
441
  * @param s String to pluralize.
430
442
  */
431
443
  const pluralize = (s) => {
432
- return s.endsWith('s') ? s :
433
- s.endsWith('y') ? `${s.slice(0, -1)}ies` :
434
- `${s}s`;
444
+ return s.endsWith('s') ? s : s.endsWith('y') ? `${s.slice(0, -1)}ies` : `${s}s`;
435
445
  };
436
446
 
437
- /* eslint-disable no-console */
438
- const logger = new class {
447
+ const logger = new (class {
439
448
  active = true;
440
449
  warnings = 0;
441
450
  errors = 0;
@@ -483,15 +492,18 @@ const logger = new class {
483
492
  printSummary() {
484
493
  const format = (v, name, fail, ok) => {
485
494
  const color = v ? fail : ok;
486
- return v === 0 ? `${color('zero')} ${pluralize(name)}` :
487
- v === 1 ? `${color('one')} ${name}` : `${color(v)} ${pluralize(name)}`;
495
+ return v === 0
496
+ ? `${color('zero')} ${pluralize(name)}`
497
+ : v === 1
498
+ ? `${color('one')} ${name}`
499
+ : `${color(v)} ${pluralize(name)}`;
488
500
  };
489
501
  const warnings = format(this.warnings, 'warning', chalk.yellowBright, chalk.greenBright);
490
502
  const errors = format(this.errors, 'error', chalk.redBright, chalk.greenBright);
491
503
  const info = `Finished with ${warnings} and ${errors}.`;
492
504
  this[this.errors ? 'errorLn' : this.warnings ? 'warnLn' : 'successLn'](info);
493
505
  }
494
- };
506
+ })();
495
507
 
496
508
  /**
497
509
  * ROOT => /article
@@ -520,12 +532,22 @@ const parseEndpointPath = (path) => {
520
532
  return { path, entity, type: WeclappEndpointType.COUNT };
521
533
  }
522
534
  else if (rest[0] === 'id') {
523
- return rest.length === 2 ?
524
- { path, entity, type: WeclappEndpointType.ENTITY } :
525
- { path, entity, method: rest[2], type: WeclappEndpointType.GENERIC_ENTITY };
535
+ return rest.length === 2
536
+ ? { path, entity, type: WeclappEndpointType.ENTITY }
537
+ : {
538
+ path,
539
+ entity,
540
+ method: rest[2],
541
+ type: WeclappEndpointType.GENERIC_ENTITY
542
+ };
526
543
  }
527
544
  else if (rest.length === 1) {
528
- return { path, entity, method: rest[1], type: WeclappEndpointType.GENERIC_ROOT };
545
+ return {
546
+ path,
547
+ entity,
548
+ method: rest[1],
549
+ type: WeclappEndpointType.GENERIC_ROOT
550
+ };
529
551
  }
530
552
  return undefined;
531
553
  };
@@ -547,12 +569,14 @@ const convertParametersToSchema = (parameters = []) => {
547
569
  if (isParameterObject(param) && param.in === 'query') {
548
570
  if (param.schema) {
549
571
  properties.push([param.name, param.schema]);
550
- param.required && required.push(param.name);
572
+ if (param.required)
573
+ required.push(param.name);
551
574
  }
552
575
  }
553
576
  }
554
577
  return {
555
- type: 'object', required,
578
+ type: 'object',
579
+ required,
556
580
  properties: Object.fromEntries(properties)
557
581
  };
558
582
  };
@@ -610,8 +634,7 @@ const generateRequestBodyType = ({ requestBody }) => {
610
634
  return generateBodyType(requestBody) ?? createRawType('unknown');
611
635
  };
612
636
 
613
- const resolveBodyType = ({ responses }) => Object.entries(responses)
614
- .filter(v => v[0].startsWith('2'))[0]?.[1];
637
+ const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
615
638
  const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
616
639
 
617
640
  const functionName$4 = 'create';
@@ -638,11 +661,7 @@ const generateCreateEndpoint = ({ target, path, endpoint }) => {
638
661
  };
639
662
 
640
663
  const generateGenericFunctionName = (path, suffix = '', prefix = '') => {
641
- return camelCase(`${prefix}_` +
642
- path
643
- .replace(/.*\//, '')
644
- .replace(/\W+/, '_')
645
- .replace(/[_]+/, '_') + `_${suffix}`);
664
+ return camelCase(`${prefix}_` + path.replace(/.*\//, '').replace(/\W+/, '_').replace(/[_]+/, '_') + `_${suffix}`);
646
665
  };
647
666
 
648
667
  const insertPathPlaceholder = (path, record) => {
@@ -650,9 +669,7 @@ const insertPathPlaceholder = (path, record) => {
650
669
  };
651
670
 
652
671
  const wrapBody = (type, target) => {
653
- return type.toString() === 'binary' ?
654
- createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') :
655
- type; // node-fetch returns a Blob as well
672
+ return type.toString() === 'binary' ? createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') : type; // node-fetch returns a Blob as well
656
673
  };
657
674
  const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
658
675
  const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
@@ -698,12 +715,12 @@ const generateRemoveEndpoint = ({ target, endpoint }) => {
698
715
  const functionSource = generateArrowFunction({
699
716
  name: functionName$3,
700
717
  signature: interfaceName,
701
- returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`)`,
702
- params: ['id']
718
+ returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
719
+ params: ['id', 'options?: RemoveQuery']
703
720
  });
704
721
  const interfaceSource = generateArrowFunctionType({
705
722
  type: interfaceName,
706
- params: ['id: string'],
723
+ params: ['id: string', 'options?: RemoveQuery'],
707
724
  returns: `${resolveResponseType(target)}<void>`
708
725
  });
709
726
  return {
@@ -715,10 +732,7 @@ const generateRemoveEndpoint = ({ target, endpoint }) => {
715
732
  };
716
733
 
717
734
  const functionName$2 = 'some';
718
- const excludedParameters = [
719
- 'page', 'pageSize', 'sort',
720
- 'serializeNulls', 'properties', 'includeReferencedEntities'
721
- ];
735
+ const excludedParameters = ['page', 'pageSize', 'sort', 'serializeNulls', 'properties', 'includeReferencedEntities'];
722
736
  const resolveAdditionalProperties = (path) => {
723
737
  const body = resolveBodyType(path);
724
738
  if (isResponseObject(body)) {
@@ -744,10 +758,11 @@ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
744
758
  const parameterSchema = convertParametersToSchema(path.parameters);
745
759
  const additionalProperties = resolveAdditionalProperties(path);
746
760
  const additionalPropertyNames = generateStrings(Object.keys(additionalProperties?.properties ?? {}));
747
- const additionalPropertyNamesType = additionalPropertyNames.length ? `(${concat(additionalPropertyNames, ' | ')})[]` : '[]';
761
+ const additionalPropertyNamesType = additionalPropertyNames.length
762
+ ? `(${concat(additionalPropertyNames, ' | ')})[]`
763
+ : '[]';
748
764
  // We already cover some properties
749
- parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {})
750
- .filter(v => !excludedParameters.includes(v[0])));
765
+ parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {}).filter((v) => !excludedParameters.includes(v[0])));
751
766
  const parameters = createObjectType({
752
767
  params: convertToTypeScriptType(parameterSchema)
753
768
  });
@@ -758,7 +773,9 @@ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
758
773
  `S extends (QuerySelect<${entity}> | undefined) = undefined`,
759
774
  `I extends (QuerySelect<${entityMappings}> | undefined) = undefined`
760
775
  ],
761
- params: [`query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S, ${additionalPropertyNamesType}> & ${entityParameters}`],
776
+ params: [
777
+ `query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S, ${additionalPropertyNamesType}> & ${entityParameters}`
778
+ ],
762
779
  returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S, ${properties}>>`
763
780
  });
764
781
  const functionSource = generateArrowFunction({
@@ -828,11 +845,19 @@ const generateUpdateEndpoint = ({ target, path, endpoint }) => {
828
845
  };
829
846
  };
830
847
 
848
+ const isMultiPartUploadPath = (path) => {
849
+ const [, entity, ...rest] = path.split('/');
850
+ return entity && rest.length === 2 && rest[1] === 'multipartUpload';
851
+ };
831
852
  const groupEndpointsByEntity = (paths) => {
832
853
  const endpoints = new Map();
833
854
  for (const [rawPath, path] of Object.entries(paths)) {
834
855
  const endpoint = parseEndpointPath(rawPath);
835
856
  if (!endpoint || !path) {
857
+ // Todo: Should be removed if sdk supports multi part upload.
858
+ if (isMultiPartUploadPath(rawPath)) {
859
+ continue;
860
+ }
836
861
  logger.errorLn(`Failed to parse ${rawPath}`);
837
862
  continue;
838
863
  }
@@ -906,8 +931,8 @@ const generateServices = (doc, aliases, options) => {
906
931
  continue;
907
932
  }
908
933
  // Construct service type
909
- const types = generateStatements(...functions.flatMap(v => v.interfaces?.map(v => v.source) ?? []), ...functions.map(v => v.type.source), generateInterface(serviceTypeName, [
910
- ...functions.map(v => ({
934
+ const types = generateStatements(...functions.flatMap((v) => v.interfaces?.map((v) => v.source) ?? []), ...functions.map((v) => v.type.source), generateInterface(serviceTypeName, [
935
+ ...functions.map((v) => ({
911
936
  required: true,
912
937
  comment: v.path.deprecated ? '@deprecated' : undefined,
913
938
  name: v.func.name,
@@ -915,11 +940,18 @@ const generateServices = (doc, aliases, options) => {
915
940
  }))
916
941
  ]));
917
942
  // Construct service value
918
- const funcBody = generateBlockStatements(...functions.map(v => v.func.source), `return {${concat(functions.map(v => v.func.name))}};`);
943
+ const funcBody = generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`);
919
944
  const func = `export const ${serviceName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${funcBody};`;
920
945
  const source = generateBlockComment(`${pascalCase(endpoint)} service`, generateStatements(types, func));
921
- const deprecated = functions.every(v => v.path.deprecated);
922
- services.set(endpoint, { entity: endpoint, deprecated, serviceName, serviceTypeName, source, functions });
946
+ const deprecated = functions.every((v) => v.path.deprecated);
947
+ services.set(endpoint, {
948
+ entity: endpoint,
949
+ deprecated,
950
+ serviceName,
951
+ serviceTypeName,
952
+ source,
953
+ functions
954
+ });
923
955
  }
924
956
  return services;
925
957
  };
@@ -932,7 +964,7 @@ const generateCustomValueUtilities = (entities, services) => {
932
964
  return '';
933
965
  }
934
966
  serviceLoop: for (const service of services) {
935
- const someFunction = service.functions.find(v => v.name === 'some');
967
+ const someFunction = service.functions.find((v) => v.name === 'some');
936
968
  if (!someFunction) {
937
969
  continue;
938
970
  }
@@ -973,22 +1005,21 @@ const resolveInheritedEntities = (root, entities) => {
973
1005
  const parent = root.extends ? entities.get(root.extends) : undefined;
974
1006
  return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
975
1007
  };
976
- const generatePropertyDescriptors = (entity, entities, services, options) => [
977
- ...resolveInheritedEntities(entity, entities).flatMap(v => [...v.properties]),
978
- ...entity.properties
979
- ].filter(([, meta]) => {
1008
+ const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
1009
+ .filter(([, meta]) => {
980
1010
  // If we generate deprecated things we can skip the filtering
981
1011
  if (options.deprecated) {
982
1012
  return true;
983
1013
  }
984
1014
  // Check if corresponding service is deprecated and can be removed
985
- const service = services.find(v => v.entity === meta.service);
1015
+ const service = services.find((v) => v.entity === meta.service);
986
1016
  return !meta.service || (service && !service.deprecated);
987
- }).map(([property, meta]) => ({
1017
+ })
1018
+ .map(([property, meta]) => ({
988
1019
  key: property,
989
1020
  value: Object.entries(meta).map(([key, value]) => ({
990
1021
  key,
991
- value: value ? generateString(value) : undefined
1022
+ value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
992
1023
  }))
993
1024
  }));
994
1025
  const generateEntityPropertyMap = (entities, services, options) => {
@@ -1001,7 +1032,7 @@ const generateEntityPropertyMap = (entities, services, options) => {
1001
1032
  };
1002
1033
 
1003
1034
  const generateArray = (values) => {
1004
- return `[${concat(values.map(v => generateString(String(v))))}]`;
1035
+ return `[${concat(values.map((v) => generateString(String(v))))}]`;
1005
1036
  };
1006
1037
 
1007
1038
  // Only functions matching this regex are included in the generation.
@@ -1020,7 +1051,8 @@ const generateGroupedServices = (services) => {
1020
1051
  continue;
1021
1052
  }
1022
1053
  entityDescriptors.set(name, [
1023
- ...(entityDescriptors.get(name) ?? []), {
1054
+ ...(entityDescriptors.get(name) ?? []),
1055
+ {
1024
1056
  name: entity,
1025
1057
  required: true,
1026
1058
  type: `${pascalCase(entity)}Service_${pascalCase(name)}`
@@ -1042,7 +1074,7 @@ const generateGroupedServices = (services) => {
1042
1074
  ...descriptors.map(([name, props]) => {
1043
1075
  const constant = camelCase(`wServiceWith_${name}_Names`);
1044
1076
  const type = pascalCase(`WServiceWith_${name}`);
1045
- const value = generateArray(props.map(v => v.name));
1077
+ const value = generateArray(props.map((v) => v.name));
1046
1078
  return `export const ${constant}: ${type}[] = ${value};`;
1047
1079
  }),
1048
1080
  generateBlockComment('Type guards for service classes.', generateStatements(...typeGuards))
@@ -1054,20 +1086,20 @@ const arr = (list) => `[\n${indent(list.join(',\n'))}\n]`;
1054
1086
  const generateMaps = ({ services, entities, aliases, enums, options }) => {
1055
1087
  const entitiesKeys = [...entities.keys()];
1056
1088
  const enumsArray = `export const wEnums = ${obj(enums)};`;
1057
- const entityNames = `export const wEntityNames: WEntity[] = ${arr(entitiesKeys.map(v => `'${v}'`))};`;
1058
- const serviceNames = `export const wServiceNames: WService[] = ${arr(services.map(v => `'${v.entity}'`))};`;
1059
- const serviceValues = `export const wServiceFactories = ${obj(services.map(v => `${v.entity}: ${v.serviceName}`))};`;
1060
- const serviceInstanceValues = `export const wServices = ${obj(services.map(v => {
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) => {
1061
1093
  const src = `${v.entity}: ${v.serviceName}()`;
1062
1094
  return v.deprecated ? generateInlineComment('@deprecated') + `\n${src}` : src;
1063
1095
  }))};`;
1064
1096
  const entityInterfaces = [
1065
- ...entitiesKeys.map(entity => ({
1097
+ ...entitiesKeys.map((entity) => ({
1066
1098
  name: entity,
1067
1099
  type: pascalCase(entity),
1068
1100
  required: true
1069
1101
  })),
1070
- ...services.map(service => {
1102
+ ...services.map((service) => {
1071
1103
  const alias = aliases.get(service.entity);
1072
1104
  return {
1073
1105
  name: service.entity,
@@ -1077,11 +1109,20 @@ const generateMaps = ({ services, entities, aliases, enums, options }) => {
1077
1109
  };
1078
1110
  })
1079
1111
  ];
1080
- const createMappingType = (type, prefix) => type !== 'never' ? `${type}_${prefix}` : type;
1112
+ const createMappingType = (type, prefix) => (type !== 'never' ? `${type}_${prefix}` : type);
1081
1113
  const entitiesList = generateInterface('WEntities', entityInterfaces);
1082
- const entityReferences = generateInterface('WEntityReferences', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'References') })));
1083
- const entityMappings = generateInterface('WEntityMappings', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Mappings') })));
1084
- const entityFilter = generateInterface('WEntityFilters', entityInterfaces.map(v => ({ ...v, type: createMappingType(v.type, 'Filter') })));
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
+ })));
1085
1126
  return {
1086
1127
  source: generateStatements(
1087
1128
  /* JS Values */
@@ -1114,7 +1155,6 @@ const generateMaps = ({ services, entities, aliases, enums, options }) => {
1114
1155
  };
1115
1156
 
1116
1157
  const parseReferencedEntity = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
1117
- /* eslint-disable @typescript-eslint/no-unsafe-return */
1118
1158
  const extractSchemas = (doc) => {
1119
1159
  const schemas = new Map();
1120
1160
  const aliases = new Map();
@@ -1161,7 +1201,7 @@ const generate = (doc, options) => {
1161
1201
  const enums = generateEnums(schemas);
1162
1202
  const entities = generateEntities(schemas, enums);
1163
1203
  const services = generateServices(doc, aliases, options);
1164
- return generateStatements(generateBase(options.target), generateBlockComment('ENUMS', generateStatements(...[...enums.values()].map(v => v.source))), generateBlockComment('ENTITIES', generateStatements(...[...entities.values()].map(v => v.source))), generateBlockComment('SERVICES', generateStatements(...[...services.values()].map(v => v.source))), generateBlockComment('MAPS', generateMaps({
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({
1165
1205
  services: [...services.values()],
1166
1206
  enums: [...enums.keys()],
1167
1207
  options,
@@ -1183,7 +1223,7 @@ const hash = (content, algorithm = 'sha256') => {
1183
1223
 
1184
1224
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
1185
1225
  const cli = async () => {
1186
- const { default: { version } } = await import('../package.json', { assert: { type: 'json' } });
1226
+ const version = pkg.version;
1187
1227
  const { argv } = yargs(hideBin(process.argv))
1188
1228
  .scriptName('build-weclapp-sdk')
1189
1229
  .usage('Usage: $0 <source> [flags]')
@@ -1228,9 +1268,8 @@ const cli = async () => {
1228
1268
  type: 'string',
1229
1269
  choices: ['browser', 'browser.rx', 'node', 'node.rx']
1230
1270
  })
1231
- .option('d', {
1232
- alias: 'deprecated',
1233
- describe: 'Include deprecated functions and services',
1271
+ .option('use-query-language', {
1272
+ describe: 'Generate the new where property for some and count queries',
1234
1273
  type: 'boolean'
1235
1274
  })
1236
1275
  .epilog(`Copyright ${new Date().getFullYear()} weclapp GmbH`);
@@ -1242,21 +1281,27 @@ const cli = async () => {
1242
1281
  const options = {
1243
1282
  deprecated,
1244
1283
  generateUnique: argv.generateUnique ?? false,
1245
- target: argv.target ?? Target.BROWSER_PROMISES
1284
+ target: argv.target ?? Target.BROWSER_PROMISES,
1285
+ useQueryLanguage: argv.useQueryLanguage ?? false
1246
1286
  };
1247
- if (typeof src === 'number') {
1248
- return Promise.reject('Expected string as command');
1287
+ if (!src || typeof src === 'number') {
1288
+ return Promise.reject(new Error('Expected string as command'));
1249
1289
  }
1250
1290
  if (!Object.values(Target).includes(options.target)) {
1251
1291
  logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(', ')}`);
1252
- return Promise.reject();
1292
+ return Promise.reject(new Error());
1253
1293
  }
1254
1294
  if (await stat(src).catch(() => false)) {
1255
- logger.infoLn(`Source is a file.`);
1295
+ logger.infoLn(`Source is a file`);
1256
1296
  const content = JSON.parse(await readFile(src, 'utf-8'));
1257
1297
  return { cache, content, options };
1258
1298
  }
1299
+ logger.infoLn(`Source is a URL`);
1300
+ if (!key) {
1301
+ return Promise.reject(new Error('API key is missing'));
1302
+ }
1259
1303
  const url = new URL(src.startsWith('http') ? src : `https://${src}`);
1304
+ // At the moment just v1
1260
1305
  url.pathname = '/webapp/api/v1/meta/openapi.json';
1261
1306
  if (query?.length) {
1262
1307
  for (const param of query.split(',')) {
@@ -1265,11 +1310,11 @@ const cli = async () => {
1265
1310
  }
1266
1311
  }
1267
1312
  const content = await fetch(url.toString(), {
1268
- headers: { 'Accept': 'application/json', 'AuthenticationToken': key }
1269
- }).then(res => res.ok ? res.json() : undefined);
1313
+ headers: { Accept: 'application/json', AuthenticationToken: key }
1314
+ }).then((res) => (res.ok ? res.json() : undefined));
1270
1315
  if (!content) {
1271
1316
  logger.errorLn(`Couldn't fetch file ${url.toString()} `);
1272
- return Promise.reject();
1317
+ return Promise.reject(new Error());
1273
1318
  }
1274
1319
  else {
1275
1320
  logger.infoLn(`Use remote file: ${url.toString()}`);
@@ -1277,50 +1322,59 @@ const cli = async () => {
1277
1322
  return { cache, content, options };
1278
1323
  };
1279
1324
 
1280
- const workingDirectory = resolve(currentDirname(), './sdk');
1281
- const folders = ['docs', 'main', 'node', 'raw', 'rx', 'utils'];
1325
+ const workingDir = resolve(currentDirname(), './sdk');
1326
+ const cacheDir = resolve(currentDirname(), './.cache');
1282
1327
  void (async () => {
1283
1328
  const start = process.hrtime.bigint();
1284
- const { default: { version } } = await import('../package.json', { assert: { type: 'json' } });
1285
1329
  const { content: doc, cache: useCache, options } = await cli();
1286
- // Resolve cache dir and key
1287
- const cacheKey = hash([version, JSON.stringify(doc), JSON.stringify(options)]).slice(-8);
1288
- const cacheDir = resolve(currentDirname(), '.tmp', cacheKey);
1289
- const dist = (...paths) => resolve(workingDirectory, ...paths);
1290
- const tmp = async (...paths) => {
1291
- const fullPath = resolve(cacheDir, ...paths);
1292
- await mkdir(dirname(fullPath), { recursive: true }).catch(() => null);
1330
+ const workingDirPath = async (...paths) => {
1331
+ const fullPath = resolve(workingDir, ...paths);
1332
+ await mkdir(dirname(fullPath), { recursive: true });
1293
1333
  return fullPath;
1294
1334
  };
1335
+ // Resolve cache dir and key
1336
+ const cacheKey = hash([pkg.version, JSON.stringify(doc), JSON.stringify(options)]).slice(-8);
1337
+ const cachedSdkDir = resolve(cacheDir, cacheKey);
1338
+ // Remove old SDK
1339
+ await rm(workingDir, { recursive: true, force: true });
1295
1340
  if (useCache) {
1296
1341
  logger.infoLn(`Cache ID: ${cacheKey}`);
1297
1342
  }
1298
- if (useCache && await stat(cacheDir).catch(() => false)) {
1299
- logger.successLn(`Cache match! (${cacheDir})`);
1343
+ if (useCache && (await stat(cachedSdkDir).catch(() => false))) {
1344
+ // Copy cached SDK to working dir
1345
+ logger.successLn(`Cache match! (${cachedSdkDir})`);
1346
+ await cp(cachedSdkDir, workingDir, { recursive: true });
1300
1347
  }
1301
1348
  else {
1302
- // Store swagger.json file
1303
- await writeFile(await tmp('openapi.json'), JSON.stringify(doc, null, 2));
1349
+ // Write openapi.json file
1350
+ await writeFile(await workingDirPath('openapi.json'), JSON.stringify(doc, null, 2));
1304
1351
  logger.infoLn(`Generate sdk (target: ${options.target})`);
1305
- // Generate SDKs
1352
+ // Generate and write SDK (index.ts)
1306
1353
  const sdk = generate(doc, options);
1307
- await writeFile(await tmp('src', `${options.target}.ts`), sdk.trim() + '\n');
1308
- // Bundle
1354
+ await writeFile(await workingDirPath('src', 'index.ts'), sdk.trim() + '\n');
1355
+ // Bundle and write SDK
1309
1356
  logger.infoLn('Bundle... (this may take some time)');
1310
- await bundle(cacheDir, options.target);
1311
- // Remove old SDK
1312
- await Promise.all(folders.map(async (dir) => rm(dist(dir), { recursive: true }).catch(() => 0)));
1357
+ await bundle(workingDir, options.target);
1358
+ // Remove index.ts (only bundle is required)
1359
+ await rm(await workingDirPath('src'), { recursive: true, force: true });
1360
+ if (useCache) {
1361
+ // Copy SDK to cache
1362
+ logger.successLn(`Caching SDK: (${cachedSdkDir})`);
1363
+ await mkdir(cachedSdkDir, { recursive: true });
1364
+ await cp(workingDir, cachedSdkDir, { recursive: true });
1365
+ }
1313
1366
  }
1314
- // Copy bundled SDK
1315
- await cp(cacheDir, workingDirectory, { recursive: true });
1316
1367
  // Print job summary
1317
1368
  const duration = (process.hrtime.bigint() - start) / 1000000n;
1318
1369
  logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
1319
1370
  logger.printSummary();
1320
- })().catch((error) => {
1371
+ })()
1372
+ .catch((error) => {
1321
1373
  logger.errorLn(`Fatal error:`);
1322
1374
  /* eslint-disable no-console */
1323
1375
  console.error(error);
1324
- }).finally(() => {
1325
- logger.errors && process.exit(1);
1376
+ })
1377
+ .finally(() => {
1378
+ if (logger.errors)
1379
+ process.exit(1);
1326
1380
  });