core-services-sdk 1.3.81 → 1.3.83

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-services-sdk",
3
- "version": "1.3.81",
3
+ "version": "1.3.83",
4
4
  "main": "src/index.js",
5
5
  "type": "module",
6
6
  "types": "types/index.d.ts",
package/src/http/http.js CHANGED
@@ -24,6 +24,7 @@
24
24
  * @property {string} url - The URL to send the request to.
25
25
  * @property {any} body - The request body to send.
26
26
  * @property {Record<string, string>} [headers] - Optional HTTP headers.
27
+ * @property {RequestInit} [extraParams] - Additional fetch options.
27
28
  * @property {ResponseTypeValue} [expectedType] - Expected response type.
28
29
  */
29
30
 
@@ -32,6 +33,7 @@
32
33
  * @property {string} url - The URL to send the request to.
33
34
  * @property {any} body - The request body to send.
34
35
  * @property {Record<string, string>} [headers] - Optional HTTP headers.
36
+ * @property {RequestInit} [extraParams] - Additional fetch options.
35
37
  * @property {ResponseTypeValue} [expectedType] - Expected response type.
36
38
  */
37
39
 
@@ -40,6 +42,7 @@
40
42
  * @property {string} url - The URL to send the request to.
41
43
  * @property {any} body - The request body to send.
42
44
  * @property {Record<string, string>} [headers] - Optional HTTP headers.
45
+ * @property {RequestInit} [extraParams] - Additional fetch options.
43
46
  * @property {ResponseTypeValue} [expectedType] - Expected response type.
44
47
  */
45
48
 
@@ -48,6 +51,7 @@
48
51
  * @property {string} url - The URL to send the request to.
49
52
  * @property {any} [body] - Optional request body to send.
50
53
  * @property {Record<string, string>} [headers] - Optional HTTP headers.
54
+ * @property {RequestInit} [extraParams] - Additional fetch options.
51
55
  * @property {ResponseTypeValue} [expectedType] - Expected response type.
52
56
  */
53
57
 
@@ -205,9 +209,11 @@ export const post = async ({
205
209
  url,
206
210
  body,
207
211
  headers = {},
212
+ extraParams = {},
208
213
  expectedType = ResponseType.json,
209
214
  }) => {
210
215
  const response = await fetch(url, {
216
+ ...extraParams,
211
217
  method: HTTP_METHODS.POST,
212
218
  headers: { ...JSON_HEADER, ...headers },
213
219
  body: JSON.stringify(body),
@@ -227,9 +233,11 @@ export const put = async ({
227
233
  url,
228
234
  body,
229
235
  headers = {},
236
+ extraParams = {},
230
237
  expectedType = ResponseType.json,
231
238
  }) => {
232
239
  const response = await fetch(url, {
240
+ ...extraParams,
233
241
  method: HTTP_METHODS.PUT,
234
242
  headers: { ...JSON_HEADER, ...headers },
235
243
  body: expectedType === ResponseType.json ? JSON.stringify(body) : body,
@@ -249,9 +257,11 @@ export const patch = async ({
249
257
  url,
250
258
  body,
251
259
  headers = {},
260
+ extraParams = {},
252
261
  expectedType = ResponseType.json,
253
262
  }) => {
254
263
  const response = await fetch(url, {
264
+ ...extraParams,
255
265
  method: HTTP_METHODS.PATCH,
256
266
  headers: { ...JSON_HEADER, ...headers },
257
267
  body: JSON.stringify(body),
@@ -271,9 +281,11 @@ export const deleteApi = async ({
271
281
  url,
272
282
  body,
273
283
  headers = {},
284
+ extraParams = {},
274
285
  expectedType = ResponseType.json,
275
286
  }) => {
276
287
  const response = await fetch(url, {
288
+ ...extraParams,
277
289
  method: HTTP_METHODS.DELETE,
278
290
  headers: { ...JSON_HEADER, ...headers },
279
291
  ...(body ? { body: JSON.stringify(body) } : {}),
@@ -5,4 +5,6 @@ export * from './filters/apply-filter.js'
5
5
  export * from './modifiers/apply-order-by.js'
6
6
  export * from './start-stop-postgres-docker.js'
7
7
  export * from './modifiers/apply-pagination.js'
8
+ export * from './repositories/BaseRepository.js'
8
9
  export * from './filters/apply-filter-snake-case.js'
10
+ export * from './repositories/TenantScopedRepository.js'
@@ -75,6 +75,7 @@ export async function sqlPaginate({
75
75
  limit = 10,
76
76
  filter = {},
77
77
  snakeCase = true,
78
+ distinctOn,
78
79
  }) {
79
80
  const listQuery = baseQuery.clone()
80
81
  const countQuery = baseQuery.clone()
@@ -89,7 +90,9 @@ export async function sqlPaginate({
89
90
 
90
91
  const [rows, countResult] = await Promise.all([
91
92
  listQuery.select('*'),
92
- countQuery.count('* as count').first(),
93
+ distinctOn
94
+ ? countQuery.countDistinct(`${distinctOn} as count`).first()
95
+ : countQuery.count('* as count').first(),
93
96
  ])
94
97
 
95
98
  const totalCount = normalizeNumberOrDefault(countResult?.count || 0)
@@ -0,0 +1,95 @@
1
+ import { sqlPaginate } from '../pagination/paginate.js'
2
+ import { toSnakeCase } from '../../core/case-mapper.js'
3
+
4
+ /**
5
+ * BaseRepository
6
+ *
7
+ * Provides:
8
+ * - tableName getter (static + instance support)
9
+ * - baseQuery(options)
10
+ * - constructor-level query shaping (optional)
11
+ * - generic find() with pagination
12
+ *
13
+ * Does NOT enforce tenant isolation.
14
+ */
15
+
16
+ export class BaseRepository {
17
+ constructor({ db, log, baseQueryBuilder } = {}) {
18
+ if (!db) {
19
+ throw new Error('BaseRepository requires db instance')
20
+ }
21
+
22
+ this.db = db
23
+ this.log = log
24
+ this._baseQueryBuilder = baseQueryBuilder
25
+ }
26
+
27
+ /**
28
+ * Each concrete repository must define:
29
+ * static tableName = 'table_name'
30
+ */
31
+ get tableName() {
32
+ if (!this.constructor.tableName) {
33
+ throw new Error(`tableName not defined for ${this.constructor.name}`)
34
+ }
35
+
36
+ return this.constructor.tableName
37
+ }
38
+
39
+ /**
40
+ * Builds the base knex query.
41
+ * Applies constructor-level baseQueryBuilder if provided.
42
+ */
43
+ baseQuery(options = {}, params) {
44
+ const { trx } = options
45
+ const connection = trx || this.db
46
+
47
+ const qb = connection(this.tableName)
48
+
49
+ if (this._baseQueryBuilder) {
50
+ // Pass full params for optional dynamic shaping
51
+ return this._baseQueryBuilder(qb, params)
52
+ }
53
+
54
+ return qb
55
+ }
56
+
57
+ /**
58
+ * Generic paginated find
59
+ */
60
+ async find({
61
+ page = 1,
62
+ limit = 10,
63
+ filter = {},
64
+ orderBy = { column: 'created_at', direction: 'desc' },
65
+ options = {},
66
+ mapRow = (row) => row,
67
+ ...restParams
68
+ }) {
69
+ try {
70
+ const qb = this.baseQuery(options, {
71
+ page,
72
+ limit,
73
+ filter,
74
+ orderBy,
75
+ options,
76
+ ...restParams,
77
+ })
78
+
79
+ return await sqlPaginate({
80
+ page,
81
+ limit,
82
+ orderBy,
83
+ baseQuery: qb,
84
+ mapRow,
85
+ filter: toSnakeCase(filter),
86
+ })
87
+ } catch (error) {
88
+ if (this.log) {
89
+ this.log.error({ error }, `Error finding from ${this.tableName}`)
90
+ }
91
+
92
+ throw error
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * TenantScopedRepository
3
+ *
4
+ * Extends BaseRepository
5
+ * Enforces tenantId presence inside filter.
6
+ *
7
+ * Does NOT modify filter.
8
+ * Does NOT rebuild filter.
9
+ * Only validates existence.
10
+ */
11
+
12
+ import { BaseRepository } from './BaseRepository.js'
13
+
14
+ export class TenantScopedRepository extends BaseRepository {
15
+ /**
16
+ * Extracts and validates tenantId from filter
17
+ */
18
+ getRequiredTenantId(filter = {}) {
19
+ const { tenantId } = filter
20
+
21
+ if (!tenantId) {
22
+ throw new Error(
23
+ `Scoped repository (${this.tableName}), tenantId is required`,
24
+ )
25
+ }
26
+
27
+ return tenantId
28
+ }
29
+
30
+ /**
31
+ * Overrides find to enforce tenant presence
32
+ */
33
+ async find(params = {}) {
34
+ const { filter = {} } = params
35
+
36
+ this.getRequiredTenantId(filter)
37
+
38
+ return super.find(params)
39
+ }
40
+ }
@@ -185,3 +185,73 @@ describe('http client (native fetch)', () => {
185
185
  })
186
186
  })
187
187
  })
188
+
189
+ describe('extraParams support', () => {
190
+ it('should pass extraParams to fetch in GET', async () => {
191
+ mockFetch.mockResolvedValueOnce(
192
+ createMockResponse({ body: JSON.stringify({ ok: true }) }),
193
+ )
194
+
195
+ const controller = new AbortController()
196
+
197
+ await http.get({
198
+ url: 'http://test.com',
199
+ extraParams: {
200
+ signal: controller.signal,
201
+ redirect: 'follow',
202
+ },
203
+ })
204
+
205
+ expect(mockFetch).toHaveBeenCalledWith(
206
+ 'http://test.com',
207
+ expect.objectContaining({
208
+ method: HTTP_METHODS.GET,
209
+ signal: controller.signal,
210
+ redirect: 'follow',
211
+ }),
212
+ )
213
+ })
214
+
215
+ it('should pass extraParams to fetch in POST', async () => {
216
+ mockFetch.mockResolvedValueOnce(
217
+ createMockResponse({ body: JSON.stringify({ ok: true }) }),
218
+ )
219
+
220
+ await http.post({
221
+ url: 'http://test.com',
222
+ body: { a: 1 },
223
+ extraParams: {
224
+ credentials: 'include',
225
+ },
226
+ })
227
+
228
+ expect(mockFetch).toHaveBeenCalledWith(
229
+ 'http://test.com',
230
+ expect.objectContaining({
231
+ method: HTTP_METHODS.POST,
232
+ credentials: 'include',
233
+ }),
234
+ )
235
+ })
236
+
237
+ it('should pass extraParams to fetch in DELETE', async () => {
238
+ mockFetch.mockResolvedValueOnce(
239
+ createMockResponse({ body: JSON.stringify({ ok: true }) }),
240
+ )
241
+
242
+ await http.deleteApi({
243
+ url: 'http://test.com',
244
+ extraParams: {
245
+ keepalive: true,
246
+ },
247
+ })
248
+
249
+ expect(mockFetch).toHaveBeenCalledWith(
250
+ 'http://test.com',
251
+ expect.objectContaining({
252
+ method: HTTP_METHODS.DELETE,
253
+ keepalive: true,
254
+ }),
255
+ )
256
+ })
257
+ })
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { BaseRepository } from '../../src/postgresql/repositories/BaseRepository.js'
3
+
4
+ // 🔥 mock dependencies
5
+ vi.mock('../../src/postgresql/pagination/paginate.js', () => ({
6
+ sqlPaginate: vi.fn().mockResolvedValue({ page: 1 }),
7
+ }))
8
+
9
+ vi.mock('../../filters/apply-filter-snake-case.js', () => ({
10
+ applyFilterSnakeCase: vi.fn(),
11
+ }))
12
+
13
+ vi.mock('../../filters/apply-filter.js', () => ({
14
+ applyFilter: vi.fn(),
15
+ }))
16
+
17
+ vi.mock('../../core/normalize-premitives-types-or-default.js', () => ({
18
+ normalizeNumberOrDefault: vi.fn((v) => Number(v)),
19
+ }))
20
+
21
+ // import AFTER mocks
22
+ import { sqlPaginate } from '../../src/postgresql/pagination/paginate.js'
23
+ import { toSnakeCase } from '../../src/core/case-mapper.js'
24
+
25
+ describe('BaseRepository', () => {
26
+ let dbMock
27
+ let logMock
28
+
29
+ class TestRepo extends BaseRepository {
30
+ static tableName = 'test_table'
31
+ }
32
+
33
+ beforeEach(() => {
34
+ dbMock = vi.fn(() => ({
35
+ clone: vi.fn(),
36
+ }))
37
+
38
+ logMock = {
39
+ error: vi.fn(),
40
+ }
41
+ })
42
+
43
+ it('throws if db not provided', () => {
44
+ expect(() => new BaseRepository()).toThrow()
45
+ })
46
+
47
+ it('returns correct tableName', () => {
48
+ const repo = new TestRepo({ db: dbMock })
49
+ expect(repo.tableName).toBe('test_table')
50
+ })
51
+
52
+ it('baseQuery uses db when no trx', () => {
53
+ const repo = new TestRepo({ db: dbMock })
54
+
55
+ repo.baseQuery()
56
+
57
+ expect(dbMock).toHaveBeenCalledWith('test_table')
58
+ })
59
+
60
+ it('baseQuery uses trx when provided', () => {
61
+ const trxMock = vi.fn()
62
+ const repo = new TestRepo({ db: dbMock })
63
+
64
+ repo.baseQuery({ trx: trxMock })
65
+
66
+ expect(trxMock).toHaveBeenCalledWith('test_table')
67
+ expect(dbMock).not.toHaveBeenCalled()
68
+ })
69
+
70
+ it('baseQuery applies baseQueryBuilder', () => {
71
+ const builderMock = vi.fn((qb) => qb)
72
+ const repo = new TestRepo({
73
+ db: dbMock,
74
+ baseQueryBuilder: builderMock,
75
+ })
76
+
77
+ repo.baseQuery({}, { some: 'param' })
78
+
79
+ expect(builderMock).toHaveBeenCalled()
80
+ })
81
+
82
+ it('find calls sqlPaginate with correct params', async () => {
83
+ const repo = new TestRepo({ db: dbMock })
84
+
85
+ await repo.find({
86
+ page: 2,
87
+ limit: 5,
88
+ filter: { a: 1 },
89
+ })
90
+
91
+ expect(sqlPaginate).toHaveBeenCalled()
92
+ })
93
+
94
+ it('logs error if sqlPaginate throws', async () => {
95
+ // @ts-ignore
96
+ sqlPaginate.mockRejectedValueOnce(new Error('fail'))
97
+
98
+ const repo = new TestRepo({ db: dbMock, log: logMock })
99
+
100
+ await expect(repo.find({ filter: {} })).rejects.toThrow()
101
+
102
+ expect(logMock.error).toHaveBeenCalled()
103
+ })
104
+ })
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { TenantScopedRepository } from '../../src/postgresql/repositories/TenantScopedRepository.js'
3
+
4
+ class TestTenantRepo extends TenantScopedRepository {
5
+ static tableName = 'tenant_table'
6
+ }
7
+
8
+ describe('TenantScopedRepository', () => {
9
+ const dbMock = vi.fn(() => ({}))
10
+
11
+ it('throws if tenantId missing', async () => {
12
+ const repo = new TestTenantRepo({ db: dbMock })
13
+
14
+ await expect(repo.find({ filter: {} })).rejects.toThrow()
15
+ })
16
+
17
+ it('does not throw if tenantId exists', async () => {
18
+ const repo = new TestTenantRepo({ db: dbMock })
19
+
20
+ // Spy on BaseRepository.find instead of overriding repo.find
21
+ const superSpy = vi
22
+ .spyOn(Object.getPrototypeOf(TenantScopedRepository.prototype), 'find')
23
+ .mockResolvedValue(true)
24
+
25
+ const result = await repo.find({
26
+ filter: { tenantId: 'abc' },
27
+ })
28
+
29
+ expect(result).toBe(true)
30
+ expect(superSpy).toHaveBeenCalled()
31
+
32
+ superSpy.mockRestore()
33
+ })
34
+
35
+ it('getRequiredTenantId returns tenantId', () => {
36
+ const repo = new TestTenantRepo({ db: dbMock })
37
+
38
+ const id = repo.getRequiredTenantId({
39
+ tenantId: 'tenant-1',
40
+ })
41
+
42
+ expect(id).toBe('tenant-1')
43
+ })
44
+ })
@@ -3,60 +3,157 @@ export function get({
3
3
  headers,
4
4
  extraParams,
5
5
  expectedType,
6
- }: {
7
- url: any
8
- headers?: {}
9
- extraParams?: {}
10
- expectedType?: 'json'
11
- }): Promise<any>
6
+ }: HttpGetOptions): Promise<any>
12
7
  export function post({
13
8
  url,
14
9
  body,
15
10
  headers,
11
+ extraParams,
16
12
  expectedType,
17
- }: {
18
- url: any
19
- body: any
20
- headers?: {}
21
- expectedType?: 'json'
22
- }): Promise<any>
13
+ }: HttpPostOptions): Promise<any>
23
14
  export function put({
24
15
  url,
25
16
  body,
26
17
  headers,
18
+ extraParams,
27
19
  expectedType,
28
- }: {
29
- url: any
30
- body: any
31
- headers?: {}
32
- expectedType?: 'json'
33
- }): Promise<any>
20
+ }: HttpPutOptions): Promise<any>
34
21
  export function patch({
35
22
  url,
36
23
  body,
37
24
  headers,
25
+ extraParams,
38
26
  expectedType,
39
- }: {
40
- url: any
41
- body: any
42
- headers?: {}
43
- expectedType?: 'json'
44
- }): Promise<any>
27
+ }: HttpPatchOptions): Promise<any>
45
28
  export function deleteApi({
46
29
  url,
47
30
  body,
48
31
  headers,
32
+ extraParams,
49
33
  expectedType,
50
- }: {
51
- url: any
34
+ }: HttpDeleteOptions): Promise<any>
35
+ /**
36
+ * Consolidated HTTP client with methods for common HTTP operations.
37
+ *
38
+ * @type {{
39
+ * get: (options: HttpGetOptions) => Promise<any>,
40
+ * put: (options: HttpPutOptions) => Promise<any>,
41
+ * post: (options: HttpPostOptions) => Promise<any>,
42
+ * patch: (options: HttpPatchOptions) => Promise<any>,
43
+ * deleteApi: (options: HttpDeleteOptions) => Promise<any>
44
+ * }}
45
+ */
46
+ export const http: {
47
+ get: (options: HttpGetOptions) => Promise<any>
48
+ put: (options: HttpPutOptions) => Promise<any>
49
+ post: (options: HttpPostOptions) => Promise<any>
50
+ patch: (options: HttpPatchOptions) => Promise<any>
51
+ deleteApi: (options: HttpDeleteOptions) => Promise<any>
52
+ }
53
+ export type ResponseTypeValue = 'json' | 'xml' | 'text' | 'raw' | 'file'
54
+ export type HttpGetOptions = {
55
+ /**
56
+ * - The URL to send the request to.
57
+ */
58
+ url: string
59
+ /**
60
+ * - Optional HTTP headers.
61
+ */
62
+ headers?: Record<string, string>
63
+ /**
64
+ * - Additional fetch options.
65
+ */
66
+ extraParams?: RequestInit
67
+ /**
68
+ * - Expected response type.
69
+ */
70
+ expectedType?: ResponseTypeValue
71
+ }
72
+ export type HttpPostOptions = {
73
+ /**
74
+ * - The URL to send the request to.
75
+ */
76
+ url: string
77
+ /**
78
+ * - The request body to send.
79
+ */
80
+ body: any
81
+ /**
82
+ * - Optional HTTP headers.
83
+ */
84
+ headers?: Record<string, string>
85
+ /**
86
+ * - Additional fetch options.
87
+ */
88
+ extraParams?: RequestInit
89
+ /**
90
+ * - Expected response type.
91
+ */
92
+ expectedType?: ResponseTypeValue
93
+ }
94
+ export type HttpPutOptions = {
95
+ /**
96
+ * - The URL to send the request to.
97
+ */
98
+ url: string
99
+ /**
100
+ * - The request body to send.
101
+ */
52
102
  body: any
53
- headers?: {}
54
- expectedType?: 'json'
55
- }): Promise<any>
56
- export namespace http {
57
- export { get }
58
- export { put }
59
- export { post }
60
- export { patch }
61
- export { deleteApi }
103
+ /**
104
+ * - Optional HTTP headers.
105
+ */
106
+ headers?: Record<string, string>
107
+ /**
108
+ * - Additional fetch options.
109
+ */
110
+ extraParams?: RequestInit
111
+ /**
112
+ * - Expected response type.
113
+ */
114
+ expectedType?: ResponseTypeValue
115
+ }
116
+ export type HttpPatchOptions = {
117
+ /**
118
+ * - The URL to send the request to.
119
+ */
120
+ url: string
121
+ /**
122
+ * - The request body to send.
123
+ */
124
+ body: any
125
+ /**
126
+ * - Optional HTTP headers.
127
+ */
128
+ headers?: Record<string, string>
129
+ /**
130
+ * - Additional fetch options.
131
+ */
132
+ extraParams?: RequestInit
133
+ /**
134
+ * - Expected response type.
135
+ */
136
+ expectedType?: ResponseTypeValue
137
+ }
138
+ export type HttpDeleteOptions = {
139
+ /**
140
+ * - The URL to send the request to.
141
+ */
142
+ url: string
143
+ /**
144
+ * - Optional request body to send.
145
+ */
146
+ body?: any
147
+ /**
148
+ * - Optional HTTP headers.
149
+ */
150
+ headers?: Record<string, string>
151
+ /**
152
+ * - Additional fetch options.
153
+ */
154
+ extraParams?: RequestInit
155
+ /**
156
+ * - Expected response type.
157
+ */
158
+ expectedType?: ResponseTypeValue
62
159
  }
@@ -5,4 +5,6 @@ export * from './filters/apply-filter.js'
5
5
  export * from './modifiers/apply-order-by.js'
6
6
  export * from './start-stop-postgres-docker.js'
7
7
  export * from './modifiers/apply-pagination.js'
8
+ export * from './repositories/BaseRepository.js'
8
9
  export * from './filters/apply-filter-snake-case.js'
10
+ export * from './repositories/TenantScopedRepository.js'
@@ -69,6 +69,7 @@ export function sqlPaginate({
69
69
  limit,
70
70
  filter,
71
71
  snakeCase,
72
+ distinctOn,
72
73
  }: {
73
74
  baseQuery: import('knex').Knex.QueryBuilder
74
75
  filter?: any
@@ -0,0 +1,50 @@
1
+ /**
2
+ * BaseRepository
3
+ *
4
+ * Provides:
5
+ * - tableName getter (static + instance support)
6
+ * - baseQuery(options)
7
+ * - constructor-level query shaping (optional)
8
+ * - generic find() with pagination
9
+ *
10
+ * Does NOT enforce tenant isolation.
11
+ */
12
+ export class BaseRepository {
13
+ constructor({ db, log, baseQueryBuilder }?: {})
14
+ db: any
15
+ log: any
16
+ _baseQueryBuilder: any
17
+ /**
18
+ * Each concrete repository must define:
19
+ * static tableName = 'table_name'
20
+ */
21
+ get tableName(): any
22
+ /**
23
+ * Builds the base knex query.
24
+ * Applies constructor-level baseQueryBuilder if provided.
25
+ */
26
+ baseQuery(options: {}, params: any): any
27
+ /**
28
+ * Generic paginated find
29
+ */
30
+ find({
31
+ page,
32
+ limit,
33
+ filter,
34
+ orderBy,
35
+ options,
36
+ mapRow,
37
+ ...restParams
38
+ }: {
39
+ [x: string]: any
40
+ page?: number
41
+ limit?: number
42
+ filter?: {}
43
+ orderBy?: {
44
+ column: string
45
+ direction: string
46
+ }
47
+ options?: {}
48
+ mapRow?: (row: any) => any
49
+ }): Promise<any>
50
+ }
@@ -0,0 +1,11 @@
1
+ export class TenantScopedRepository extends BaseRepository {
2
+ /**
3
+ * Extracts and validates tenantId from filter
4
+ */
5
+ getRequiredTenantId(filter?: {}): any
6
+ /**
7
+ * Overrides find to enforce tenant presence
8
+ */
9
+ find(params?: {}): Promise<any>
10
+ }
11
+ import { BaseRepository } from './BaseRepository.js'