@ikatec/digisac-api-sdk 3.1.0 → 4.1.0

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 (139) hide show
  1. package/README.md +71 -9
  2. package/dist/apis/authHistory/AuthHistoryApi.cjs +3 -9
  3. package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
  4. package/dist/apis/authHistory/AuthHistoryApi.d.ts +3 -4
  5. package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
  6. package/dist/apis/authHistory/AuthHistoryApi.mjs +3 -7
  7. package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
  8. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs +2 -5
  9. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs.map +1 -1
  10. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts +2 -4
  11. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts.map +1 -1
  12. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs +3 -5
  13. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs.map +1 -1
  14. package/dist/apis/contacts/ContactsApi.cjs +2 -2
  15. package/dist/apis/contacts/ContactsApi.cjs.map +1 -1
  16. package/dist/apis/contacts/ContactsApi.mjs +2 -2
  17. package/dist/apis/contacts/ContactsApi.mjs.map +1 -1
  18. package/dist/apis/me/MeApi.cjs +2 -5
  19. package/dist/apis/me/MeApi.cjs.map +1 -1
  20. package/dist/apis/me/MeApi.d.ts +2 -4
  21. package/dist/apis/me/MeApi.d.ts.map +1 -1
  22. package/dist/apis/me/MeApi.mjs +3 -5
  23. package/dist/apis/me/MeApi.mjs.map +1 -1
  24. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs +3 -9
  25. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs.map +1 -1
  26. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts +3 -4
  27. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts.map +1 -1
  28. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs +3 -7
  29. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs.map +1 -1
  30. package/dist/apis/stickerUsers/StickerUsersApi.cjs +3 -9
  31. package/dist/apis/stickerUsers/StickerUsersApi.cjs.map +1 -1
  32. package/dist/apis/stickerUsers/StickerUsersApi.d.ts +3 -4
  33. package/dist/apis/stickerUsers/StickerUsersApi.d.ts.map +1 -1
  34. package/dist/apis/stickerUsers/StickerUsersApi.mjs +3 -7
  35. package/dist/apis/stickerUsers/StickerUsersApi.mjs.map +1 -1
  36. package/dist/apis/terms/TermsApi.cjs +3 -12
  37. package/dist/apis/terms/TermsApi.cjs.map +1 -1
  38. package/dist/apis/terms/TermsApi.d.ts +2 -4
  39. package/dist/apis/terms/TermsApi.d.ts.map +1 -1
  40. package/dist/apis/terms/TermsApi.mjs +3 -10
  41. package/dist/apis/terms/TermsApi.mjs.map +1 -1
  42. package/dist/apis/transcripts/TranscriptsApi.cjs +2 -5
  43. package/dist/apis/transcripts/TranscriptsApi.cjs.map +1 -1
  44. package/dist/apis/transcripts/TranscriptsApi.d.ts +2 -4
  45. package/dist/apis/transcripts/TranscriptsApi.d.ts.map +1 -1
  46. package/dist/apis/transcripts/TranscriptsApi.mjs +3 -5
  47. package/dist/apis/transcripts/TranscriptsApi.mjs.map +1 -1
  48. package/dist/core/ApiClient.d.ts +3 -0
  49. package/dist/core/ApiClient.d.ts.map +1 -1
  50. package/dist/core/ArchivableCrudApi.cjs +0 -2
  51. package/dist/core/ArchivableCrudApi.cjs.map +1 -1
  52. package/dist/core/ArchivableCrudApi.d.ts.map +1 -1
  53. package/dist/core/ArchivableCrudApi.mjs +0 -2
  54. package/dist/core/ArchivableCrudApi.mjs.map +1 -1
  55. package/dist/core/BaseApi.cjs +34 -0
  56. package/dist/core/BaseApi.cjs.map +1 -0
  57. package/dist/core/BaseApi.d.ts +20 -0
  58. package/dist/core/BaseApi.d.ts.map +1 -0
  59. package/dist/core/BaseApi.mjs +33 -0
  60. package/dist/core/BaseApi.mjs.map +1 -0
  61. package/dist/core/BaseApiClient.cjs +29 -57
  62. package/dist/core/BaseApiClient.cjs.map +1 -1
  63. package/dist/core/BaseApiClient.d.ts +8 -11
  64. package/dist/core/BaseApiClient.d.ts.map +1 -1
  65. package/dist/core/BaseApiClient.mjs +31 -57
  66. package/dist/core/BaseApiClient.mjs.map +1 -1
  67. package/dist/core/BaseCrudApi.cjs +6 -16
  68. package/dist/core/BaseCrudApi.cjs.map +1 -1
  69. package/dist/core/BaseCrudApi.d.ts +3 -2
  70. package/dist/core/BaseCrudApi.d.ts.map +1 -1
  71. package/dist/core/BaseCrudApi.mjs +6 -14
  72. package/dist/core/BaseCrudApi.mjs.map +1 -1
  73. package/dist/core/bindMethods.cjs +24 -0
  74. package/dist/core/bindMethods.cjs.map +1 -0
  75. package/dist/core/bindMethods.d.ts +7 -0
  76. package/dist/core/bindMethods.d.ts.map +1 -0
  77. package/dist/core/bindMethods.mjs +22 -0
  78. package/dist/core/bindMethods.mjs.map +1 -0
  79. package/dist/core/errors.cjs +192 -0
  80. package/dist/core/errors.cjs.map +1 -0
  81. package/dist/core/errors.d.ts +117 -0
  82. package/dist/core/errors.d.ts.map +1 -0
  83. package/dist/core/errors.mjs +177 -0
  84. package/dist/core/errors.mjs.map +1 -0
  85. package/dist/core/index.cjs +18 -2
  86. package/dist/core/index.d.ts +6 -2
  87. package/dist/core/index.d.ts.map +1 -1
  88. package/dist/core/index.mjs +4 -2
  89. package/dist/core/queryString.cjs +22 -0
  90. package/dist/core/queryString.cjs.map +1 -0
  91. package/dist/core/queryString.d.ts +14 -0
  92. package/dist/core/queryString.d.ts.map +1 -0
  93. package/dist/core/queryString.mjs +18 -0
  94. package/dist/core/queryString.mjs.map +1 -0
  95. package/dist/core/types.d.ts +2 -0
  96. package/dist/core/types.d.ts.map +1 -1
  97. package/dist/core/withQueryFormat.cjs +42 -0
  98. package/dist/core/withQueryFormat.cjs.map +1 -0
  99. package/dist/core/withQueryFormat.d.ts +10 -0
  100. package/dist/core/withQueryFormat.d.ts.map +1 -0
  101. package/dist/core/withQueryFormat.mjs +40 -0
  102. package/dist/core/withQueryFormat.mjs.map +1 -0
  103. package/dist/index.cjs +18 -2
  104. package/dist/index.mjs +4 -2
  105. package/package.json +1 -1
  106. package/src/apis/authHistory/AuthHistoryApi.test.ts +1 -1
  107. package/src/apis/authHistory/AuthHistoryApi.ts +10 -10
  108. package/src/apis/campaigns/CampaignsApi.test.ts +1 -1
  109. package/src/apis/clientFeedback/ClientFeedbackApi.ts +5 -7
  110. package/src/apis/contacts/ContactsApi.test.ts +1 -1
  111. package/src/apis/contacts/ContactsApi.ts +2 -2
  112. package/src/apis/me/MeApi.ts +2 -8
  113. package/src/apis/messages/MessagesApi.test.ts +2 -2
  114. package/src/apis/queryFormat.test.ts +198 -0
  115. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.test.ts +2 -2
  116. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.ts +10 -10
  117. package/src/apis/stickerUsers/StickerUsersApi.test.ts +1 -1
  118. package/src/apis/stickerUsers/StickerUsersApi.ts +7 -10
  119. package/src/apis/terms/TermsApi.ts +3 -12
  120. package/src/apis/transcripts/TranscriptsApi.ts +2 -7
  121. package/src/core/ApiClient.ts +4 -0
  122. package/src/core/ArchivableCrudApi.ts +0 -2
  123. package/src/core/BaseApi.test.ts +164 -0
  124. package/src/core/BaseApi.ts +32 -0
  125. package/src/core/BaseApiClient.test.ts +193 -22
  126. package/src/core/BaseApiClient.ts +44 -85
  127. package/src/core/BaseCrudApi.test-d.ts +57 -0
  128. package/src/core/BaseCrudApi.test.ts +252 -31
  129. package/src/core/BaseCrudApi.ts +14 -14
  130. package/src/core/bindMethods.ts +26 -0
  131. package/src/core/errors.test-d.ts +14 -0
  132. package/src/core/errors.test.ts +389 -0
  133. package/src/core/errors.ts +284 -0
  134. package/src/core/index.ts +21 -2
  135. package/src/core/queryString.test.ts +71 -0
  136. package/src/core/queryString.ts +30 -0
  137. package/src/core/types.ts +5 -0
  138. package/src/core/withQueryFormat.test.ts +136 -0
  139. package/src/core/withQueryFormat.ts +47 -0
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\n/**\n * Digisac field errors: `{ errors: { <location>: { <field>: { messages: string[], types: string[] } } } }`,\n * where location is `body`, `params`, `query`, `headers` or `cookies` (backend `validate` middleware).\n */\ntype DigisacFieldErrors = Record<string, Record<string, { messages?: unknown; types?: unknown }>>\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with\n * `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).\n * Returns `null` when the body carries no field errors.\n */\nfunction parseValidationErrors(errors: unknown): ValidationError[] | null {\n if (!isRecord(errors)) {\n return null\n }\n\n const parsed: ValidationError[] = []\n for (const [location, fields] of Object.entries(errors as DigisacFieldErrors)) {\n if (!isRecord(fields)) {\n continue\n }\n for (const [field, detail] of Object.entries(fields)) {\n const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : []\n const types = Array.isArray(detail?.types) ? detail.types.map(String) : []\n messages.forEach((message, index) => {\n parsed.push({ path: `${location}.${field}`, message, code: types[index] ?? types[0] ?? '' })\n })\n }\n }\n\n return parsed.length ? parsed : null\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors)\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BaseApiClient.cjs","names":["DEFAULT_QUERY_FORMAT","ApiConnectionError","ApiError"],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\nimport { ApiConnectionError, ApiError } from './errors'\nimport { DEFAULT_QUERY_FORMAT, type QueryFormat } from './queryString'\n\nexport interface BaseApiClientOptions {\n /** How list/get queries are serialized in the URL. Defaults to `'json'`. */\n queryFormat?: QueryFormat\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n public readonly queryFormat: QueryFormat\n\n constructor(baseUrl: string, accessToken?: string, options: BaseApiClientOptions = {}) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n this.queryFormat = options.queryFormat ?? DEFAULT_QUERY_FORMAT\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n const init: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n }\n\n let response: Response\n try {\n response = await fetch(url, init)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new ApiConnectionError(`Request to ${method} ${url} failed: ${reason}`, {\n cause: error,\n })\n }\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n throw ApiError.fromResponse(\n response.status,\n response.statusText,\n response.headers,\n await this.readErrorBody(response),\n )\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */\n protected async readErrorBody(response: Response): Promise<unknown> {\n let text: string\n try {\n text = await response.text()\n } catch {\n return undefined\n }\n if (!text) {\n return undefined\n }\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;;;AASA,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CACvC,AAAgB;CAEhB,YAAY,SAAiB,aAAsB,UAAgC,EAAE,EAAE;AACrF,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;AAClC,OAAK,cAAc,QAAQ,eAAeA;;CAG5C,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAC1D,MAAM,OAAoB;GACxB;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD;EAED,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,MAAM,KAAK,KAAK;WAC1B,OAAO;AAEd,SAAM,IAAIC,uCAAmB,cAAc,OAAO,GAAG,IAAI,WAD1C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACS,EAC5E,OAAO,OACR,CAAC;;AAGJ,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,GACZ,OAAMC,6BAAS,aACb,SAAS,QACT,SAAS,YACT,SAAS,SACT,MAAM,KAAK,cAAc,SAAS,CACnC;AAIH,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;CAKX,MAAgB,cAAc,UAAsC;EAClE,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,MAAM;UACtB;AACN;;AAEF,MAAI,CAAC,KACH;AAEF,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
@@ -1,22 +1,19 @@
1
1
  import type { ApiClient, HttpMethod } from './ApiClient';
2
- export interface ValidationError {
3
- path: string;
4
- message: string;
5
- code: string;
6
- }
7
- export declare class ApiError extends Error {
8
- readonly errorClass?: string | undefined;
9
- readonly status?: number | undefined;
10
- readonly validationErrors: ValidationError[] | null;
11
- constructor(message: string, errorClass?: string, status?: number, validationErrors?: ValidationError[] | null);
2
+ import { type QueryFormat } from './queryString';
3
+ export interface BaseApiClientOptions {
4
+ /** How list/get queries are serialized in the URL. Defaults to `'json'`. */
5
+ queryFormat?: QueryFormat;
12
6
  }
13
7
  export declare class BaseApiClient implements ApiClient {
14
8
  protected readonly baseUrl: string;
15
9
  protected accessToken: string | null;
16
- constructor(baseUrl: string, accessToken?: string);
10
+ readonly queryFormat: QueryFormat;
11
+ constructor(baseUrl: string, accessToken?: string, options?: BaseApiClientOptions);
17
12
  setAccessToken(accessToken: string): this;
18
13
  request<TResponse = unknown, TBody = unknown>(method: HttpMethod, endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
19
14
  protected handleResponse<TResponse>(response: Response): Promise<TResponse>;
15
+ /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */
16
+ protected readErrorBody(response: Response): Promise<unknown>;
20
17
  get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse>;
21
18
  post<TResponse = unknown, TBody = unknown>(endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
22
19
  put<TResponse = unknown, TBody = unknown>(endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAsCD,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/C,SAAgB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,SAAgB,gBAAgB,EAAE,eAAe,EAAE,GAAG,IAAI,CAAA;gBAGxD,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,GAAE,eAAe,EAAE,GAAG,IAAW;CAQpD;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE/B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAKjD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAgBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuDjF,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
1
+ {"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAEtE,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;IAC3C,SAAgB,WAAW,EAAE,WAAW,CAAA;gBAE5B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB;IAMrF,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAyBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuCjF,sGAAsG;cACtF,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBnE,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
@@ -1,47 +1,15 @@
1
+ import { ApiConnectionError, ApiError } from "./errors.mjs";
2
+ import { DEFAULT_QUERY_FORMAT } from "./queryString.mjs";
3
+
1
4
  //#region src/core/BaseApiClient.ts
2
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3
- /**
4
- * Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with
5
- * `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).
6
- * Returns `null` when the body carries no field errors.
7
- */
8
- function parseValidationErrors(errors) {
9
- if (!isRecord(errors)) return null;
10
- const parsed = [];
11
- for (const [location, fields] of Object.entries(errors)) {
12
- if (!isRecord(fields)) continue;
13
- for (const [field, detail] of Object.entries(fields)) {
14
- const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : [];
15
- const types = Array.isArray(detail?.types) ? detail.types.map(String) : [];
16
- messages.forEach((message, index) => {
17
- parsed.push({
18
- path: `${location}.${field}`,
19
- message,
20
- code: types[index] ?? types[0] ?? ""
21
- });
22
- });
23
- }
24
- }
25
- return parsed.length ? parsed : null;
26
- }
27
- var ApiError = class extends Error {
28
- errorClass;
29
- status;
30
- validationErrors;
31
- constructor(message, errorClass, status, validationErrors = null) {
32
- super(message);
33
- this.name = "ApiError";
34
- this.errorClass = errorClass;
35
- this.status = status;
36
- this.validationErrors = validationErrors;
37
- }
38
- };
39
5
  var BaseApiClient = class {
40
6
  baseUrl;
41
7
  accessToken = null;
42
- constructor(baseUrl, accessToken) {
8
+ queryFormat;
9
+ constructor(baseUrl, accessToken, options = {}) {
43
10
  this.baseUrl = baseUrl.replace(/\/$/, "");
44
11
  this.accessToken = accessToken || null;
12
+ this.queryFormat = options.queryFormat ?? DEFAULT_QUERY_FORMAT;
45
13
  }
46
14
  setAccessToken(accessToken) {
47
15
  this.accessToken = accessToken;
@@ -49,7 +17,7 @@ var BaseApiClient = class {
49
17
  }
50
18
  async request(method, endpoint, body, headers) {
51
19
  const url = `${this.baseUrl}/${endpoint.replace(/^\//, "")}`;
52
- const response = await fetch(url, {
20
+ const init = {
53
21
  method,
54
22
  headers: {
55
23
  "Content-Type": "application/json",
@@ -57,26 +25,17 @@ var BaseApiClient = class {
57
25
  ...headers
58
26
  },
59
27
  ...body !== void 0 && { body: JSON.stringify(body) }
60
- });
28
+ };
29
+ let response;
30
+ try {
31
+ response = await fetch(url, init);
32
+ } catch (error) {
33
+ throw new ApiConnectionError(`Request to ${method} ${url} failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
34
+ }
61
35
  return this.handleResponse(response);
62
36
  }
63
37
  async handleResponse(response) {
64
- if (!response.ok) {
65
- const isJson = response.headers.get("Content-Type")?.includes("application/json") ?? false;
66
- let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`;
67
- let errorClass;
68
- let validationErrors = null;
69
- if (isJson) try {
70
- const errorText = await response.text();
71
- if (errorText) {
72
- const errorData = JSON.parse(errorText);
73
- errorMessage = errorData.message || errorData.error || errorMessage;
74
- errorClass = errorData.error;
75
- validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors);
76
- }
77
- } catch {}
78
- throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
79
- }
38
+ if (!response.ok) throw ApiError.fromResponse(response.status, response.statusText, response.headers, await this.readErrorBody(response));
80
39
  if (response.status === 204) return;
81
40
  const text = await response.text();
82
41
  if (!text) return;
@@ -87,6 +46,21 @@ var BaseApiClient = class {
87
46
  return text;
88
47
  }
89
48
  }
49
+ /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */
50
+ async readErrorBody(response) {
51
+ let text;
52
+ try {
53
+ text = await response.text();
54
+ } catch {
55
+ return;
56
+ }
57
+ if (!text) return;
58
+ try {
59
+ return JSON.parse(text);
60
+ } catch {
61
+ return text;
62
+ }
63
+ }
90
64
  get(endpoint, headers) {
91
65
  return this.request("GET", endpoint, void 0, headers);
92
66
  }
@@ -105,5 +79,5 @@ var BaseApiClient = class {
105
79
  };
106
80
 
107
81
  //#endregion
108
- export { ApiError, BaseApiClient };
82
+ export { BaseApiClient };
109
83
  //# sourceMappingURL=BaseApiClient.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\n/**\n * Digisac field errors: `{ errors: { <location>: { <field>: { messages: string[], types: string[] } } } }`,\n * where location is `body`, `params`, `query`, `headers` or `cookies` (backend `validate` middleware).\n */\ntype DigisacFieldErrors = Record<string, Record<string, { messages?: unknown; types?: unknown }>>\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with\n * `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).\n * Returns `null` when the body carries no field errors.\n */\nfunction parseValidationErrors(errors: unknown): ValidationError[] | null {\n if (!isRecord(errors)) {\n return null\n }\n\n const parsed: ValidationError[] = []\n for (const [location, fields] of Object.entries(errors as DigisacFieldErrors)) {\n if (!isRecord(fields)) {\n continue\n }\n for (const [field, detail] of Object.entries(fields)) {\n const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : []\n const types = Array.isArray(detail?.types) ? detail.types.map(String) : []\n messages.forEach((message, index) => {\n parsed.push({ path: `${location}.${field}`, message, code: types[index] ?? types[0] ?? '' })\n })\n }\n }\n\n return parsed.length ? parsed : null\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors)\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\nimport { ApiConnectionError, ApiError } from './errors'\nimport { DEFAULT_QUERY_FORMAT, type QueryFormat } from './queryString'\n\nexport interface BaseApiClientOptions {\n /** How list/get queries are serialized in the URL. Defaults to `'json'`. */\n queryFormat?: QueryFormat\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n public readonly queryFormat: QueryFormat\n\n constructor(baseUrl: string, accessToken?: string, options: BaseApiClientOptions = {}) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n this.queryFormat = options.queryFormat ?? DEFAULT_QUERY_FORMAT\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n const init: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n }\n\n let response: Response\n try {\n response = await fetch(url, init)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new ApiConnectionError(`Request to ${method} ${url} failed: ${reason}`, {\n cause: error,\n })\n }\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n throw ApiError.fromResponse(\n response.status,\n response.statusText,\n response.headers,\n await this.readErrorBody(response),\n )\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */\n protected async readErrorBody(response: Response): Promise<unknown> {\n let text: string\n try {\n text = await response.text()\n } catch {\n return undefined\n }\n if (!text) {\n return undefined\n }\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;;AASA,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CACvC,AAAgB;CAEhB,YAAY,SAAiB,aAAsB,UAAgC,EAAE,EAAE;AACrF,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;AAClC,OAAK,cAAc,QAAQ,eAAe;;CAG5C,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAC1D,MAAM,OAAoB;GACxB;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD;EAED,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,MAAM,KAAK,KAAK;WAC1B,OAAO;AAEd,SAAM,IAAI,mBAAmB,cAAc,OAAO,GAAG,IAAI,WAD1C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACS,EAC5E,OAAO,OACR,CAAC;;AAGJ,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,GACZ,OAAM,SAAS,aACb,SAAS,QACT,SAAS,YACT,SAAS,SACT,MAAM,KAAK,cAAc,SAAS,CACnC;AAIH,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;CAKX,MAAgB,cAAc,UAAsC;EAClE,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,MAAM;UACtB;AACN;;AAEF,MAAI,CAAC,KACH;AAEF,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
@@ -1,24 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_chunk = require('../_chunks/chunk-Dk0Otj32.cjs');
3
- let qs = require("qs");
4
- qs = require_chunk.__toESM(qs);
2
+ const require_core_BaseApi = require('./BaseApi.cjs');
5
3
 
6
4
  //#region src/core/BaseCrudApi.ts
7
- var BaseCrudApi = class {
8
- client;
5
+ var BaseCrudApi = class extends require_core_BaseApi.BaseApi {
9
6
  basePath;
10
7
  constructor(client, basePath) {
11
- this.client = client;
8
+ super(client);
12
9
  this.basePath = basePath.replace(/\/$/, "");
13
- this.getMany = this.getMany.bind(this);
14
- this.getOne = this.getOne.bind(this);
15
- this.getById = this.getById.bind(this);
16
- this.create = this.create.bind(this);
17
- this.updateById = this.updateById.bind(this);
18
- this.deleteById = this.deleteById.bind(this);
19
10
  }
20
11
  getMany(query, headers) {
21
- const queryString = query ? `?${qs.default.stringify(query)}` : "";
12
+ const queryString = this.toQueryString(query);
22
13
  return this.client.get(`${this.basePath}${queryString}`, headers);
23
14
  }
24
15
  /**
@@ -31,15 +22,14 @@ var BaseCrudApi = class {
31
22
  ...query,
32
23
  limit: 1,
33
24
  paginate: false
34
- }, headers))[0] ?? null;
25
+ }, headers))?.[0] ?? null;
35
26
  }
36
27
  /**
37
28
  * Returns a single resource by ID.
38
29
  * @permissions $resourceName.view
39
30
  */
40
31
  getById(id, query, headers) {
41
- const queryString = query ? `?${qs.default.stringify(query)}` : "";
42
- return this.client.get(`${this.basePath}/${id}${queryString}`, headers);
32
+ return this.client.get(`${this.basePath}/${id}${this.toQueryString(query)}`, headers);
43
33
  }
44
34
  /**
45
35
  * Creates a new resource.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseCrudApi.cjs","names":[],"sources":["../../src/core/BaseCrudApi.ts"],"sourcesContent":["import qs from 'qs'\nimport type { ApiClient } from './ApiClient'\nimport type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types'\nexport type {\n WhereClause,\n IncludeItem,\n ListQuery,\n GetByIdQuery,\n Paginated,\n HasGetMany,\n HasGetById,\n HasCreate,\n HasUpdateById,\n HasDeleteById,\n CrudApi,\n} from './types'\n\nexport class BaseCrudApi<\n TResponse,\n TCreate,\n TUpdate = TCreate,\n TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,\n> implements CrudApi<TResponse, TCreate, TUpdate, TQuery> {\n public readonly client: ApiClient\n public readonly basePath: string\n\n constructor(client: ApiClient, basePath: string) {\n this.client = client\n this.basePath = basePath.replace(/\\/$/, '')\n this.getMany = this.getMany.bind(this)\n this.getOne = this.getOne.bind(this)\n this.getById = this.getById.bind(this)\n this.create = this.create.bind(this)\n this.updateById = this.updateById.bind(this)\n this.deleteById = this.deleteById.bind(this)\n }\n\n getMany(\n query: TQuery & { paginate: false },\n headers?: Record<string, string>,\n ): Promise<TResponse[]>\n getMany(\n query?: TQuery & { paginate?: true },\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse>>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]> {\n const queryString = query ? `?${qs.stringify(query)}` : ''\n return this.client.get<Paginated<TResponse> | TResponse[]>(\n `${this.basePath}${queryString}`,\n headers,\n )\n }\n\n /**\n * Returns the first resource matching the query, or `null` when none match.\n * Convenience over `getMany` with `limit: 1`.\n * @permissions $resourceName.view\n */\n async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {\n const results = await this.getMany(\n { ...query, limit: 1, paginate: false } as TQuery & { paginate: false },\n headers,\n )\n return results[0] ?? null\n }\n\n /**\n * Returns a single resource by ID.\n * @permissions $resourceName.view\n */\n getById(\n id: string,\n query?: GetByIdQuery<TResponse>,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const queryString = query ? `?${qs.stringify(query)}` : ''\n return this.client.get<TResponse>(`${this.basePath}/${id}${queryString}`, headers)\n }\n\n /**\n * Creates a new resource.\n * @permissions $resourceName.create\n */\n create(body: TCreate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.post<TResponse, TCreate>(this.basePath, body, headers)\n }\n\n /**\n * Updates an existing resource by ID.\n * @permissions $resourceName.update\n */\n updateById(id: string, body: TUpdate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.put<TResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)\n }\n\n /**\n * Deletes a resource by ID.\n * @permissions $resourceName.delete\n */\n deleteById(id: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.delete<TResponse>(`${this.basePath}/${id}`, headers)\n }\n}\n"],"mappings":";;;;;;AAiBA,IAAa,cAAb,MAK0D;CACxD,AAAgB;CAChB,AAAgB;CAEhB,YAAY,QAAmB,UAAkB;AAC/C,OAAK,SAAS;AACd,OAAK,WAAW,SAAS,QAAQ,OAAO,GAAG;AAC3C,OAAK,UAAU,KAAK,QAAQ,KAAK,KAAK;AACtC,OAAK,SAAS,KAAK,OAAO,KAAK,KAAK;AACpC,OAAK,UAAU,KAAK,QAAQ,KAAK,KAAK;AACtC,OAAK,SAAS,KAAK,OAAO,KAAK,KAAK;AACpC,OAAK,aAAa,KAAK,WAAW,KAAK,KAAK;AAC5C,OAAK,aAAa,KAAK,WAAW,KAAK,KAAK;;CAW9C,QACE,OACA,SAC6C;EAC7C,MAAM,cAAc,QAAQ,IAAI,WAAG,UAAU,MAAM,KAAK;AACxD,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,WAAW,eACnB,QACD;;;;;;;CAQH,MAAM,OAAO,OAAgB,SAA6D;AAKxF,UAJgB,MAAM,KAAK,QACzB;GAAE,GAAG;GAAO,OAAO;GAAG,UAAU;GAAO,EACvC,QACD,EACc,MAAM;;;;;;CAOvB,QACE,IACA,OACA,SACoB;EACpB,MAAM,cAAc,QAAQ,IAAI,WAAG,UAAU,MAAM,KAAK;AACxD,SAAO,KAAK,OAAO,IAAe,GAAG,KAAK,SAAS,GAAG,KAAK,eAAe,QAAQ;;;;;;CAOpF,OAAO,MAAe,SAAsD;AAC1E,SAAO,KAAK,OAAO,KAAyB,KAAK,UAAU,MAAM,QAAQ;;;;;;CAO3E,WAAW,IAAY,MAAe,SAAsD;AAC1F,SAAO,KAAK,OAAO,IAAwB,GAAG,KAAK,SAAS,GAAG,MAAM,MAAM,QAAQ;;;;;;CAOrF,WAAW,IAAY,SAAsD;AAC3E,SAAO,KAAK,OAAO,OAAkB,GAAG,KAAK,SAAS,GAAG,MAAM,QAAQ"}
1
+ {"version":3,"file":"BaseCrudApi.cjs","names":["BaseApi"],"sources":["../../src/core/BaseCrudApi.ts"],"sourcesContent":["import type { ApiClient } from './ApiClient'\nimport { BaseApi } from './BaseApi'\nimport type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types'\nexport type {\n WhereClause,\n IncludeItem,\n ListQuery,\n GetByIdQuery,\n Paginated,\n HasGetMany,\n HasGetById,\n HasCreate,\n HasUpdateById,\n HasDeleteById,\n CrudApi,\n} from './types'\n\nexport class BaseCrudApi<\n TResponse,\n TCreate,\n TUpdate = TCreate,\n TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,\n>\n extends BaseApi\n implements CrudApi<TResponse, TCreate, TUpdate, TQuery>\n{\n public readonly basePath: string\n\n constructor(client: ApiClient, basePath: string) {\n super(client)\n this.basePath = basePath.replace(/\\/$/, '')\n }\n\n getMany(\n query: TQuery & { paginate: false },\n headers?: Record<string, string>,\n ): Promise<TResponse[]>\n getMany(\n query?: TQuery & { paginate?: true },\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse>>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]> {\n const queryString = this.toQueryString(query)\n return this.client.get<Paginated<TResponse> | TResponse[]>(\n `${this.basePath}${queryString}`,\n headers,\n )\n }\n\n /**\n * Returns the first resource matching the query, or `null` when none match.\n * Convenience over `getMany` with `limit: 1`.\n * @permissions $resourceName.view\n */\n async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {\n const results = await this.getMany(\n { ...query, limit: 1, paginate: false } as TQuery & { paginate: false },\n headers,\n )\n // An empty response body resolves to `undefined`.\n return results?.[0] ?? null\n }\n\n /**\n * Returns a single resource by ID.\n * @permissions $resourceName.view\n */\n getById(\n id: string,\n query?: GetByIdQuery<TResponse>,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.client.get<TResponse>(`${this.basePath}/${id}${this.toQueryString(query)}`, headers)\n }\n\n /**\n * Creates a new resource.\n * @permissions $resourceName.create\n */\n create(body: TCreate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.post<TResponse, TCreate>(this.basePath, body, headers)\n }\n\n /**\n * Updates an existing resource by ID.\n * @permissions $resourceName.update\n */\n updateById(id: string, body: TUpdate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.put<TResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)\n }\n\n /**\n * Deletes a resource by ID.\n * @permissions $resourceName.delete\n */\n deleteById(id: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.delete<TResponse>(`${this.basePath}/${id}`, headers)\n }\n}\n"],"mappings":";;;;AAiBA,IAAa,cAAb,cAMUA,6BAEV;CACE,AAAgB;CAEhB,YAAY,QAAmB,UAAkB;AAC/C,QAAM,OAAO;AACb,OAAK,WAAW,SAAS,QAAQ,OAAO,GAAG;;CAe7C,QACE,OACA,SAC6C;EAC7C,MAAM,cAAc,KAAK,cAAc,MAAM;AAC7C,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,WAAW,eACnB,QACD;;;;;;;CAQH,MAAM,OAAO,OAAgB,SAA6D;AAMxF,UALgB,MAAM,KAAK,QACzB;GAAE,GAAG;GAAO,OAAO;GAAG,UAAU;GAAO,EACvC,QACD,IAEgB,MAAM;;;;;;CAOzB,QACE,IACA,OACA,SACoB;AACpB,SAAO,KAAK,OAAO,IAAe,GAAG,KAAK,SAAS,GAAG,KAAK,KAAK,cAAc,MAAM,IAAI,QAAQ;;;;;;CAOlG,OAAO,MAAe,SAAsD;AAC1E,SAAO,KAAK,OAAO,KAAyB,KAAK,UAAU,MAAM,QAAQ;;;;;;CAO3E,WAAW,IAAY,MAAe,SAAsD;AAC1F,SAAO,KAAK,OAAO,IAAwB,GAAG,KAAK,SAAS,GAAG,MAAM,MAAM,QAAQ;;;;;;CAOrF,WAAW,IAAY,SAAsD;AAC3E,SAAO,KAAK,OAAO,OAAkB,GAAG,KAAK,SAAS,GAAG,MAAM,QAAQ"}
@@ -1,8 +1,8 @@
1
1
  import type { ApiClient } from './ApiClient';
2
+ import { BaseApi } from './BaseApi';
2
3
  import type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types';
3
4
  export type { WhereClause, IncludeItem, ListQuery, GetByIdQuery, Paginated, HasGetMany, HasGetById, HasCreate, HasUpdateById, HasDeleteById, CrudApi, } from './types';
4
- export declare class BaseCrudApi<TResponse, TCreate, TUpdate = TCreate, TQuery extends ListQuery<TResponse> = ListQuery<TResponse>> implements CrudApi<TResponse, TCreate, TUpdate, TQuery> {
5
- readonly client: ApiClient;
5
+ export declare class BaseCrudApi<TResponse, TCreate, TUpdate = TCreate, TQuery extends ListQuery<TResponse> = ListQuery<TResponse>> extends BaseApi implements CrudApi<TResponse, TCreate, TUpdate, TQuery> {
6
6
  readonly basePath: string;
7
7
  constructor(client: ApiClient, basePath: string);
8
8
  getMany(query: TQuery & {
@@ -11,6 +11,7 @@ export declare class BaseCrudApi<TResponse, TCreate, TUpdate = TCreate, TQuery e
11
11
  getMany(query?: TQuery & {
12
12
  paginate?: true;
13
13
  }, headers?: Record<string, string>): Promise<Paginated<TResponse>>;
14
+ getMany(query?: TQuery, headers?: Record<string, string>): Promise<Paginated<TResponse> | TResponse[]>;
14
15
  /**
15
16
  * Returns the first resource matching the query, or `null` when none match.
16
17
  * Convenience over `getMany` with `limit: 1`.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseCrudApi.d.ts","sourceRoot":"","sources":["../../src/core/BaseCrudApi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAC1E,YAAY,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,OAAO,GACR,MAAM,SAAS,CAAA;AAEhB,qBAAa,WAAW,CACtB,SAAS,EACT,OAAO,EACP,OAAO,GAAG,OAAO,EACjB,MAAM,SAAS,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAC1D,YAAW,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IACvD,SAAgB,MAAM,EAAE,SAAS,CAAA;IACjC,SAAgB,QAAQ,EAAE,MAAM,CAAA;gBAEpB,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM;IAW/C,OAAO,CACL,KAAK,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,EACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,EAAE,CAAC;IACvB,OAAO,CACL,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,EAAE,IAAI,CAAA;KAAE,EACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAYhC;;;;OAIG;IACG,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAQzF;;;OAGG;IACH,OAAO,CACL,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,EAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAKrB;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3E;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3F;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;CAG7E"}
1
+ {"version":3,"file":"BaseCrudApi.d.ts","sourceRoot":"","sources":["../../src/core/BaseCrudApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAC1E,YAAY,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,OAAO,GACR,MAAM,SAAS,CAAA;AAEhB,qBAAa,WAAW,CACtB,SAAS,EACT,OAAO,EACP,OAAO,GAAG,OAAO,EACjB,MAAM,SAAS,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAE1D,SAAQ,OACR,YAAW,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IAEvD,SAAgB,QAAQ,EAAE,MAAM,CAAA;gBAEpB,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM;IAK/C,OAAO,CACL,KAAK,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,EACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,EAAE,CAAC;IACvB,OAAO,CACL,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,EAAE,IAAI,CAAA;KAAE,EACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CACL,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,EAAE,CAAC;IAY9C;;;;OAIG;IACG,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IASzF;;;OAGG;IACH,OAAO,CACL,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,EAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3E;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3F;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;CAG7E"}
@@ -1,21 +1,14 @@
1
- import qs from "qs";
1
+ import { BaseApi } from "./BaseApi.mjs";
2
2
 
3
3
  //#region src/core/BaseCrudApi.ts
4
- var BaseCrudApi = class {
5
- client;
4
+ var BaseCrudApi = class extends BaseApi {
6
5
  basePath;
7
6
  constructor(client, basePath) {
8
- this.client = client;
7
+ super(client);
9
8
  this.basePath = basePath.replace(/\/$/, "");
10
- this.getMany = this.getMany.bind(this);
11
- this.getOne = this.getOne.bind(this);
12
- this.getById = this.getById.bind(this);
13
- this.create = this.create.bind(this);
14
- this.updateById = this.updateById.bind(this);
15
- this.deleteById = this.deleteById.bind(this);
16
9
  }
17
10
  getMany(query, headers) {
18
- const queryString = query ? `?${qs.stringify(query)}` : "";
11
+ const queryString = this.toQueryString(query);
19
12
  return this.client.get(`${this.basePath}${queryString}`, headers);
20
13
  }
21
14
  /**
@@ -28,15 +21,14 @@ var BaseCrudApi = class {
28
21
  ...query,
29
22
  limit: 1,
30
23
  paginate: false
31
- }, headers))[0] ?? null;
24
+ }, headers))?.[0] ?? null;
32
25
  }
33
26
  /**
34
27
  * Returns a single resource by ID.
35
28
  * @permissions $resourceName.view
36
29
  */
37
30
  getById(id, query, headers) {
38
- const queryString = query ? `?${qs.stringify(query)}` : "";
39
- return this.client.get(`${this.basePath}/${id}${queryString}`, headers);
31
+ return this.client.get(`${this.basePath}/${id}${this.toQueryString(query)}`, headers);
40
32
  }
41
33
  /**
42
34
  * Creates a new resource.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseCrudApi.mjs","names":[],"sources":["../../src/core/BaseCrudApi.ts"],"sourcesContent":["import qs from 'qs'\nimport type { ApiClient } from './ApiClient'\nimport type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types'\nexport type {\n WhereClause,\n IncludeItem,\n ListQuery,\n GetByIdQuery,\n Paginated,\n HasGetMany,\n HasGetById,\n HasCreate,\n HasUpdateById,\n HasDeleteById,\n CrudApi,\n} from './types'\n\nexport class BaseCrudApi<\n TResponse,\n TCreate,\n TUpdate = TCreate,\n TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,\n> implements CrudApi<TResponse, TCreate, TUpdate, TQuery> {\n public readonly client: ApiClient\n public readonly basePath: string\n\n constructor(client: ApiClient, basePath: string) {\n this.client = client\n this.basePath = basePath.replace(/\\/$/, '')\n this.getMany = this.getMany.bind(this)\n this.getOne = this.getOne.bind(this)\n this.getById = this.getById.bind(this)\n this.create = this.create.bind(this)\n this.updateById = this.updateById.bind(this)\n this.deleteById = this.deleteById.bind(this)\n }\n\n getMany(\n query: TQuery & { paginate: false },\n headers?: Record<string, string>,\n ): Promise<TResponse[]>\n getMany(\n query?: TQuery & { paginate?: true },\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse>>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]> {\n const queryString = query ? `?${qs.stringify(query)}` : ''\n return this.client.get<Paginated<TResponse> | TResponse[]>(\n `${this.basePath}${queryString}`,\n headers,\n )\n }\n\n /**\n * Returns the first resource matching the query, or `null` when none match.\n * Convenience over `getMany` with `limit: 1`.\n * @permissions $resourceName.view\n */\n async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {\n const results = await this.getMany(\n { ...query, limit: 1, paginate: false } as TQuery & { paginate: false },\n headers,\n )\n return results[0] ?? null\n }\n\n /**\n * Returns a single resource by ID.\n * @permissions $resourceName.view\n */\n getById(\n id: string,\n query?: GetByIdQuery<TResponse>,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const queryString = query ? `?${qs.stringify(query)}` : ''\n return this.client.get<TResponse>(`${this.basePath}/${id}${queryString}`, headers)\n }\n\n /**\n * Creates a new resource.\n * @permissions $resourceName.create\n */\n create(body: TCreate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.post<TResponse, TCreate>(this.basePath, body, headers)\n }\n\n /**\n * Updates an existing resource by ID.\n * @permissions $resourceName.update\n */\n updateById(id: string, body: TUpdate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.put<TResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)\n }\n\n /**\n * Deletes a resource by ID.\n * @permissions $resourceName.delete\n */\n deleteById(id: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.delete<TResponse>(`${this.basePath}/${id}`, headers)\n }\n}\n"],"mappings":";;;AAiBA,IAAa,cAAb,MAK0D;CACxD,AAAgB;CAChB,AAAgB;CAEhB,YAAY,QAAmB,UAAkB;AAC/C,OAAK,SAAS;AACd,OAAK,WAAW,SAAS,QAAQ,OAAO,GAAG;AAC3C,OAAK,UAAU,KAAK,QAAQ,KAAK,KAAK;AACtC,OAAK,SAAS,KAAK,OAAO,KAAK,KAAK;AACpC,OAAK,UAAU,KAAK,QAAQ,KAAK,KAAK;AACtC,OAAK,SAAS,KAAK,OAAO,KAAK,KAAK;AACpC,OAAK,aAAa,KAAK,WAAW,KAAK,KAAK;AAC5C,OAAK,aAAa,KAAK,WAAW,KAAK,KAAK;;CAW9C,QACE,OACA,SAC6C;EAC7C,MAAM,cAAc,QAAQ,IAAI,GAAG,UAAU,MAAM,KAAK;AACxD,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,WAAW,eACnB,QACD;;;;;;;CAQH,MAAM,OAAO,OAAgB,SAA6D;AAKxF,UAJgB,MAAM,KAAK,QACzB;GAAE,GAAG;GAAO,OAAO;GAAG,UAAU;GAAO,EACvC,QACD,EACc,MAAM;;;;;;CAOvB,QACE,IACA,OACA,SACoB;EACpB,MAAM,cAAc,QAAQ,IAAI,GAAG,UAAU,MAAM,KAAK;AACxD,SAAO,KAAK,OAAO,IAAe,GAAG,KAAK,SAAS,GAAG,KAAK,eAAe,QAAQ;;;;;;CAOpF,OAAO,MAAe,SAAsD;AAC1E,SAAO,KAAK,OAAO,KAAyB,KAAK,UAAU,MAAM,QAAQ;;;;;;CAO3E,WAAW,IAAY,MAAe,SAAsD;AAC1F,SAAO,KAAK,OAAO,IAAwB,GAAG,KAAK,SAAS,GAAG,MAAM,MAAM,QAAQ;;;;;;CAOrF,WAAW,IAAY,SAAsD;AAC3E,SAAO,KAAK,OAAO,OAAkB,GAAG,KAAK,SAAS,GAAG,MAAM,QAAQ"}
1
+ {"version":3,"file":"BaseCrudApi.mjs","names":[],"sources":["../../src/core/BaseCrudApi.ts"],"sourcesContent":["import type { ApiClient } from './ApiClient'\nimport { BaseApi } from './BaseApi'\nimport type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types'\nexport type {\n WhereClause,\n IncludeItem,\n ListQuery,\n GetByIdQuery,\n Paginated,\n HasGetMany,\n HasGetById,\n HasCreate,\n HasUpdateById,\n HasDeleteById,\n CrudApi,\n} from './types'\n\nexport class BaseCrudApi<\n TResponse,\n TCreate,\n TUpdate = TCreate,\n TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,\n>\n extends BaseApi\n implements CrudApi<TResponse, TCreate, TUpdate, TQuery>\n{\n public readonly basePath: string\n\n constructor(client: ApiClient, basePath: string) {\n super(client)\n this.basePath = basePath.replace(/\\/$/, '')\n }\n\n getMany(\n query: TQuery & { paginate: false },\n headers?: Record<string, string>,\n ): Promise<TResponse[]>\n getMany(\n query?: TQuery & { paginate?: true },\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse>>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]> {\n const queryString = this.toQueryString(query)\n return this.client.get<Paginated<TResponse> | TResponse[]>(\n `${this.basePath}${queryString}`,\n headers,\n )\n }\n\n /**\n * Returns the first resource matching the query, or `null` when none match.\n * Convenience over `getMany` with `limit: 1`.\n * @permissions $resourceName.view\n */\n async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {\n const results = await this.getMany(\n { ...query, limit: 1, paginate: false } as TQuery & { paginate: false },\n headers,\n )\n // An empty response body resolves to `undefined`.\n return results?.[0] ?? null\n }\n\n /**\n * Returns a single resource by ID.\n * @permissions $resourceName.view\n */\n getById(\n id: string,\n query?: GetByIdQuery<TResponse>,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.client.get<TResponse>(`${this.basePath}/${id}${this.toQueryString(query)}`, headers)\n }\n\n /**\n * Creates a new resource.\n * @permissions $resourceName.create\n */\n create(body: TCreate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.post<TResponse, TCreate>(this.basePath, body, headers)\n }\n\n /**\n * Updates an existing resource by ID.\n * @permissions $resourceName.update\n */\n updateById(id: string, body: TUpdate, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.put<TResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)\n }\n\n /**\n * Deletes a resource by ID.\n * @permissions $resourceName.delete\n */\n deleteById(id: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.client.delete<TResponse>(`${this.basePath}/${id}`, headers)\n }\n}\n"],"mappings":";;;AAiBA,IAAa,cAAb,cAMU,QAEV;CACE,AAAgB;CAEhB,YAAY,QAAmB,UAAkB;AAC/C,QAAM,OAAO;AACb,OAAK,WAAW,SAAS,QAAQ,OAAO,GAAG;;CAe7C,QACE,OACA,SAC6C;EAC7C,MAAM,cAAc,KAAK,cAAc,MAAM;AAC7C,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,WAAW,eACnB,QACD;;;;;;;CAQH,MAAM,OAAO,OAAgB,SAA6D;AAMxF,UALgB,MAAM,KAAK,QACzB;GAAE,GAAG;GAAO,OAAO;GAAG,UAAU;GAAO,EACvC,QACD,IAEgB,MAAM;;;;;;CAOzB,QACE,IACA,OACA,SACoB;AACpB,SAAO,KAAK,OAAO,IAAe,GAAG,KAAK,SAAS,GAAG,KAAK,KAAK,cAAc,MAAM,IAAI,QAAQ;;;;;;CAOlG,OAAO,MAAe,SAAsD;AAC1E,SAAO,KAAK,OAAO,KAAyB,KAAK,UAAU,MAAM,QAAQ;;;;;;CAO3E,WAAW,IAAY,MAAe,SAAsD;AAC1F,SAAO,KAAK,OAAO,IAAwB,GAAG,KAAK,SAAS,GAAG,MAAM,MAAM,QAAQ;;;;;;CAOrF,WAAW,IAAY,SAAsD;AAC3E,SAAO,KAAK,OAAO,OAAkB,GAAG,KAAK,SAAS,GAAG,MAAM,QAAQ"}
@@ -0,0 +1,24 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ //#region src/core/bindMethods.ts
4
+ /**
5
+ * Binds every method of `instance`'s class chain (up to `Object.prototype`) to `instance`, as own
6
+ * properties, so methods keep working when destructured (`const { getMany } = api`). The most
7
+ * derived implementation wins; getters, setters and `constructor` are skipped.
8
+ */
9
+ function bindMethods(instance) {
10
+ const target = instance;
11
+ const bound = /* @__PURE__ */ new Set();
12
+ for (let prototype = Object.getPrototypeOf(instance); prototype && prototype !== Object.prototype; prototype = Object.getPrototypeOf(prototype)) for (const key of Reflect.ownKeys(prototype)) {
13
+ if (key === "constructor" || bound.has(key)) continue;
14
+ const { value } = Object.getOwnPropertyDescriptor(prototype, key);
15
+ if (typeof value === "function") {
16
+ target[key] = value.bind(instance);
17
+ bound.add(key);
18
+ }
19
+ }
20
+ }
21
+
22
+ //#endregion
23
+ exports.bindMethods = bindMethods;
24
+ //# sourceMappingURL=bindMethods.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindMethods.cjs","names":[],"sources":["../../src/core/bindMethods.ts"],"sourcesContent":["/**\n * Binds every method of `instance`'s class chain (up to `Object.prototype`) to `instance`, as own\n * properties, so methods keep working when destructured (`const { getMany } = api`). The most\n * derived implementation wins; getters, setters and `constructor` are skipped.\n */\nexport function bindMethods(instance: object): void {\n const target = instance as Record<PropertyKey, unknown>\n const bound = new Set<PropertyKey>()\n\n for (\n let prototype: object | null = Object.getPrototypeOf(instance);\n prototype && prototype !== Object.prototype;\n prototype = Object.getPrototypeOf(prototype)\n ) {\n for (const key of Reflect.ownKeys(prototype)) {\n if (key === 'constructor' || bound.has(key)) {\n continue\n }\n const { value } = Object.getOwnPropertyDescriptor(prototype, key) as PropertyDescriptor\n if (typeof value === 'function') {\n target[key] = value.bind(instance)\n bound.add(key)\n }\n }\n }\n}\n"],"mappings":";;;;;;;;AAKA,SAAgB,YAAY,UAAwB;CAClD,MAAM,SAAS;CACf,MAAM,wBAAQ,IAAI,KAAkB;AAEpC,MACE,IAAI,YAA2B,OAAO,eAAe,SAAS,EAC9D,aAAa,cAAc,OAAO,WAClC,YAAY,OAAO,eAAe,UAAU,CAE5C,MAAK,MAAM,OAAO,QAAQ,QAAQ,UAAU,EAAE;AAC5C,MAAI,QAAQ,iBAAiB,MAAM,IAAI,IAAI,CACzC;EAEF,MAAM,EAAE,UAAU,OAAO,yBAAyB,WAAW,IAAI;AACjE,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAO,OAAO,MAAM,KAAK,SAAS;AAClC,SAAM,IAAI,IAAI"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Binds every method of `instance`'s class chain (up to `Object.prototype`) to `instance`, as own
3
+ * properties, so methods keep working when destructured (`const { getMany } = api`). The most
4
+ * derived implementation wins; getters, setters and `constructor` are skipped.
5
+ */
6
+ export declare function bindMethods(instance: object): void;
7
+ //# sourceMappingURL=bindMethods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindMethods.d.ts","sourceRoot":"","sources":["../../src/core/bindMethods.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAoBlD"}
@@ -0,0 +1,22 @@
1
+ //#region src/core/bindMethods.ts
2
+ /**
3
+ * Binds every method of `instance`'s class chain (up to `Object.prototype`) to `instance`, as own
4
+ * properties, so methods keep working when destructured (`const { getMany } = api`). The most
5
+ * derived implementation wins; getters, setters and `constructor` are skipped.
6
+ */
7
+ function bindMethods(instance) {
8
+ const target = instance;
9
+ const bound = /* @__PURE__ */ new Set();
10
+ for (let prototype = Object.getPrototypeOf(instance); prototype && prototype !== Object.prototype; prototype = Object.getPrototypeOf(prototype)) for (const key of Reflect.ownKeys(prototype)) {
11
+ if (key === "constructor" || bound.has(key)) continue;
12
+ const { value } = Object.getOwnPropertyDescriptor(prototype, key);
13
+ if (typeof value === "function") {
14
+ target[key] = value.bind(instance);
15
+ bound.add(key);
16
+ }
17
+ }
18
+ }
19
+
20
+ //#endregion
21
+ export { bindMethods };
22
+ //# sourceMappingURL=bindMethods.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindMethods.mjs","names":[],"sources":["../../src/core/bindMethods.ts"],"sourcesContent":["/**\n * Binds every method of `instance`'s class chain (up to `Object.prototype`) to `instance`, as own\n * properties, so methods keep working when destructured (`const { getMany } = api`). The most\n * derived implementation wins; getters, setters and `constructor` are skipped.\n */\nexport function bindMethods(instance: object): void {\n const target = instance as Record<PropertyKey, unknown>\n const bound = new Set<PropertyKey>()\n\n for (\n let prototype: object | null = Object.getPrototypeOf(instance);\n prototype && prototype !== Object.prototype;\n prototype = Object.getPrototypeOf(prototype)\n ) {\n for (const key of Reflect.ownKeys(prototype)) {\n if (key === 'constructor' || bound.has(key)) {\n continue\n }\n const { value } = Object.getOwnPropertyDescriptor(prototype, key) as PropertyDescriptor\n if (typeof value === 'function') {\n target[key] = value.bind(instance)\n bound.add(key)\n }\n }\n }\n}\n"],"mappings":";;;;;;AAKA,SAAgB,YAAY,UAAwB;CAClD,MAAM,SAAS;CACf,MAAM,wBAAQ,IAAI,KAAkB;AAEpC,MACE,IAAI,YAA2B,OAAO,eAAe,SAAS,EAC9D,aAAa,cAAc,OAAO,WAClC,YAAY,OAAO,eAAe,UAAU,CAE5C,MAAK,MAAM,OAAO,QAAQ,QAAQ,UAAU,EAAE;AAC5C,MAAI,QAAQ,iBAAiB,MAAM,IAAI,IAAI,CACzC;EAEF,MAAM,EAAE,UAAU,OAAO,yBAAyB,WAAW,IAAI;AACjE,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAO,OAAO,MAAM,KAAK,SAAS;AAClC,SAAM,IAAI,IAAI"}