@budibase/worker 2.6.18 → 2.6.19-alpha.1
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/jest.config.ts +6 -5
- package/nodemon.json +6 -2
- package/package.json +6 -6
- package/src/api/controllers/global/users.ts +17 -16
- package/src/api/routes/global/tests/groups.spec.ts +176 -0
- package/src/tests/api/groups.ts +32 -2
- package/src/tests/structures/groups.ts +19 -4
- package/src/utilities/redis.ts +1 -1
- package/tsconfig.json +2 -6
package/jest.config.ts
CHANGED
|
@@ -16,15 +16,16 @@ const config: Config.InitialOptions = {
|
|
|
16
16
|
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
|
|
17
17
|
"@budibase/backend-core": "<rootDir>/../backend-core/src",
|
|
18
18
|
"@budibase/types": "<rootDir>/../types/src",
|
|
19
|
+
"@budibase/shared-core": ["<rootDir>/../shared-core/src"],
|
|
19
20
|
},
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
// add pro sources if they exist
|
|
23
|
-
if (fs.existsSync("
|
|
24
|
-
config.moduleNameMapper["@budibase/pro/(.*)"] =
|
|
25
|
-
"<rootDir
|
|
26
|
-
config.moduleNameMapper["@budibase/pro"] =
|
|
27
|
-
"<rootDir
|
|
24
|
+
if (fs.existsSync("../pro/packages")) {
|
|
25
|
+
config.moduleNameMapper!["@budibase/pro/(.*)"] =
|
|
26
|
+
"<rootDir>/../pro/packages/pro/$1"
|
|
27
|
+
config.moduleNameMapper!["@budibase/pro"] =
|
|
28
|
+
"<rootDir>/../pro/packages/pro/src"
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export default config
|
package/nodemon.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"watch": ["src", "../backend-core", "
|
|
2
|
+
"watch": ["src", "../backend-core", "../pro/packages/pro"],
|
|
3
3
|
"ext": "js,ts,json",
|
|
4
|
-
"ignore": [
|
|
4
|
+
"ignore": [
|
|
5
|
+
"src/**/*.spec.ts",
|
|
6
|
+
"src/**/*.spec.js",
|
|
7
|
+
"../backend-core/dist/**/*"
|
|
8
|
+
],
|
|
5
9
|
"exec": "ts-node src/index.ts"
|
|
6
10
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.19-alpha.1",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"author": "Budibase",
|
|
38
38
|
"license": "GPL-3.0",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@budibase/backend-core": "
|
|
41
|
-
"@budibase/pro": "2.6.
|
|
42
|
-
"@budibase/string-templates": "
|
|
43
|
-
"@budibase/types": "
|
|
40
|
+
"@budibase/backend-core": "2.6.19-alpha.1",
|
|
41
|
+
"@budibase/pro": "2.6.19-alpha.1",
|
|
42
|
+
"@budibase/string-templates": "2.6.19-alpha.1",
|
|
43
|
+
"@budibase/types": "2.6.19-alpha.1",
|
|
44
44
|
"@koa/router": "8.0.8",
|
|
45
45
|
"@sentry/node": "6.17.7",
|
|
46
46
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"typescript": "4.7.3",
|
|
103
103
|
"update-dotenv": "1.1.1"
|
|
104
104
|
},
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "0c1a7f3a6d7229024e20646bd75ab450abf2f9bb"
|
|
106
106
|
}
|
|
@@ -69,9 +69,11 @@ const bulkCreate = async (users: User[], groupIds: string[]) => {
|
|
|
69
69
|
return await userSdk.bulkCreate(users, groupIds)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export const bulkUpdate = async (
|
|
72
|
+
export const bulkUpdate = async (
|
|
73
|
+
ctx: Ctx<BulkUserRequest, BulkUserResponse>
|
|
74
|
+
) => {
|
|
73
75
|
const currentUserId = ctx.user._id
|
|
74
|
-
const input = ctx.request.body
|
|
76
|
+
const input = ctx.request.body
|
|
75
77
|
let created, deleted
|
|
76
78
|
try {
|
|
77
79
|
if (input.create) {
|
|
@@ -83,7 +85,7 @@ export const bulkUpdate = async (ctx: any) => {
|
|
|
83
85
|
} catch (err: any) {
|
|
84
86
|
ctx.throw(err.status || 400, err?.message || err)
|
|
85
87
|
}
|
|
86
|
-
ctx.body = { created, deleted }
|
|
88
|
+
ctx.body = { created, deleted }
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
const parseBooleanParam = (param: any) => {
|
|
@@ -184,15 +186,15 @@ export const destroy = async (ctx: any) => {
|
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
export const getAppUsers = async (ctx:
|
|
188
|
-
const body = ctx.request.body
|
|
189
|
+
export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
190
|
+
const body = ctx.request.body
|
|
189
191
|
const users = await userSdk.getUsersByAppAccess(body?.appId)
|
|
190
192
|
|
|
191
193
|
ctx.body = { data: users }
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
export const search = async (ctx:
|
|
195
|
-
const body = ctx.request.body
|
|
196
|
+
export const search = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
197
|
+
const body = ctx.request.body
|
|
196
198
|
|
|
197
199
|
if (body.paginated === false) {
|
|
198
200
|
await getAppUsers(ctx)
|
|
@@ -238,8 +240,8 @@ export const tenantUserLookup = async (ctx: any) => {
|
|
|
238
240
|
/*
|
|
239
241
|
Encapsulate the app user onboarding flows here.
|
|
240
242
|
*/
|
|
241
|
-
export const onboardUsers = async (ctx:
|
|
242
|
-
const request = ctx.request.body
|
|
243
|
+
export const onboardUsers = async (ctx: Ctx<InviteUsersRequest>) => {
|
|
244
|
+
const request = ctx.request.body
|
|
243
245
|
const isBulkCreate = "create" in request
|
|
244
246
|
|
|
245
247
|
const emailConfigured = await isEmailConfigured()
|
|
@@ -255,7 +257,7 @@ export const onboardUsers = async (ctx: any) => {
|
|
|
255
257
|
} else if (emailConfigured) {
|
|
256
258
|
onboardingResponse = await inviteMultiple(ctx)
|
|
257
259
|
} else if (!emailConfigured) {
|
|
258
|
-
const inviteRequest = ctx.request.body
|
|
260
|
+
const inviteRequest = ctx.request.body
|
|
259
261
|
|
|
260
262
|
let createdPasswords: any = {}
|
|
261
263
|
|
|
@@ -295,10 +297,10 @@ export const onboardUsers = async (ctx: any) => {
|
|
|
295
297
|
}
|
|
296
298
|
}
|
|
297
299
|
|
|
298
|
-
export const invite = async (ctx:
|
|
299
|
-
const request = ctx.request.body
|
|
300
|
+
export const invite = async (ctx: Ctx<InviteUserRequest>) => {
|
|
301
|
+
const request = ctx.request.body
|
|
300
302
|
|
|
301
|
-
let multiRequest = [request]
|
|
303
|
+
let multiRequest = [request]
|
|
302
304
|
const response = await userSdk.invite(multiRequest)
|
|
303
305
|
|
|
304
306
|
// explicitly throw for single user invite
|
|
@@ -318,8 +320,8 @@ export const invite = async (ctx: any) => {
|
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
|
|
321
|
-
export const inviteMultiple = async (ctx:
|
|
322
|
-
const request = ctx.request.body
|
|
323
|
+
export const inviteMultiple = async (ctx: Ctx<InviteUsersRequest>) => {
|
|
324
|
+
const request = ctx.request.body
|
|
323
325
|
ctx.body = await userSdk.invite(request)
|
|
324
326
|
}
|
|
325
327
|
|
|
@@ -424,7 +426,6 @@ export const inviteAccept = async (
|
|
|
424
426
|
if (err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) {
|
|
425
427
|
// explicitly re-throw limit exceeded errors
|
|
426
428
|
ctx.throw(400, err)
|
|
427
|
-
return
|
|
428
429
|
}
|
|
429
430
|
console.warn("Error inviting user", err)
|
|
430
431
|
ctx.throw(400, "Unable to create new user, invitation invalid.")
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { events } from "@budibase/backend-core"
|
|
2
|
+
import { generator } from "@budibase/backend-core/tests"
|
|
2
3
|
import { structures, TestConfiguration, mocks } from "../../../../tests"
|
|
4
|
+
import { UserGroup } from "@budibase/types"
|
|
5
|
+
|
|
6
|
+
mocks.licenses.useGroups()
|
|
3
7
|
|
|
4
8
|
describe("/api/global/groups", () => {
|
|
5
9
|
const config = new TestConfiguration()
|
|
@@ -13,6 +17,7 @@ describe("/api/global/groups", () => {
|
|
|
13
17
|
})
|
|
14
18
|
|
|
15
19
|
beforeEach(async () => {
|
|
20
|
+
jest.resetAllMocks()
|
|
16
21
|
mocks.licenses.useGroups()
|
|
17
22
|
})
|
|
18
23
|
|
|
@@ -24,6 +29,63 @@ describe("/api/global/groups", () => {
|
|
|
24
29
|
expect(events.group.updated).not.toBeCalled()
|
|
25
30
|
expect(events.group.permissionsEdited).not.toBeCalled()
|
|
26
31
|
})
|
|
32
|
+
|
|
33
|
+
it("should not allow undefined names", async () => {
|
|
34
|
+
const group = { ...structures.groups.UserGroup(), name: undefined } as any
|
|
35
|
+
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
36
|
+
expect(JSON.parse(response.text).message).toEqual(
|
|
37
|
+
'Invalid body - "name" is required'
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("should not allow empty names", async () => {
|
|
42
|
+
const group = { ...structures.groups.UserGroup(), name: "" }
|
|
43
|
+
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
44
|
+
expect(JSON.parse(response.text).message).toEqual(
|
|
45
|
+
'Invalid body - "name" is not allowed to be empty'
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("should not allow whitespace names", async () => {
|
|
50
|
+
const group = { ...structures.groups.UserGroup(), name: " " }
|
|
51
|
+
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
52
|
+
expect(JSON.parse(response.text).message).toEqual(
|
|
53
|
+
'Invalid body - "name" is not allowed to be empty'
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("should trim names", async () => {
|
|
58
|
+
const group = { ...structures.groups.UserGroup(), name: " group name " }
|
|
59
|
+
await config.api.groups.saveGroup(group)
|
|
60
|
+
expect(events.group.created).toBeCalledWith(
|
|
61
|
+
expect.objectContaining({ name: "group name" })
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe("name max length", () => {
|
|
66
|
+
const maxLength = 50
|
|
67
|
+
|
|
68
|
+
it(`should allow names shorter than ${maxLength} characters`, async () => {
|
|
69
|
+
const group = {
|
|
70
|
+
...structures.groups.UserGroup(),
|
|
71
|
+
name: structures.generator.word({ length: maxLength }),
|
|
72
|
+
}
|
|
73
|
+
await config.api.groups.saveGroup(group, { expect: 200 })
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it(`should not allow names longer than ${maxLength} characters`, async () => {
|
|
77
|
+
const group = {
|
|
78
|
+
...structures.groups.UserGroup(),
|
|
79
|
+
name: structures.generator.word({ length: maxLength + 1 }),
|
|
80
|
+
}
|
|
81
|
+
const response = await config.api.groups.saveGroup(group, {
|
|
82
|
+
expect: 400,
|
|
83
|
+
})
|
|
84
|
+
expect(JSON.parse(response.text).message).toEqual(
|
|
85
|
+
'Invalid body - "name" length must be less than or equal to 50 characters long'
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
27
89
|
})
|
|
28
90
|
|
|
29
91
|
describe("update", () => {
|
|
@@ -55,4 +117,118 @@ describe("/api/global/groups", () => {
|
|
|
55
117
|
})
|
|
56
118
|
})
|
|
57
119
|
})
|
|
120
|
+
|
|
121
|
+
describe("find users", () => {
|
|
122
|
+
describe("without users", () => {
|
|
123
|
+
let group: UserGroup
|
|
124
|
+
beforeAll(async () => {
|
|
125
|
+
group = structures.groups.UserGroup()
|
|
126
|
+
await config.api.groups.saveGroup(group)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("should return empty", async () => {
|
|
130
|
+
const result = await config.api.groups.searchUsers(group._id!)
|
|
131
|
+
expect(result.body).toEqual({
|
|
132
|
+
users: [],
|
|
133
|
+
bookmark: undefined,
|
|
134
|
+
hasNextPage: false,
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe("existing users", () => {
|
|
140
|
+
let groupId: string
|
|
141
|
+
let users: { _id: string; email: string }[] = []
|
|
142
|
+
|
|
143
|
+
beforeAll(async () => {
|
|
144
|
+
groupId = (
|
|
145
|
+
await config.api.groups.saveGroup(structures.groups.UserGroup())
|
|
146
|
+
).body._id
|
|
147
|
+
|
|
148
|
+
await Promise.all(
|
|
149
|
+
Array.from({ length: 30 }).map(async (_, i) => {
|
|
150
|
+
const email = `user${i}@${generator.domain()}`
|
|
151
|
+
const user = await config.api.users.saveUser({
|
|
152
|
+
...structures.users.user(),
|
|
153
|
+
email,
|
|
154
|
+
})
|
|
155
|
+
users.push({ _id: user.body._id, email })
|
|
156
|
+
})
|
|
157
|
+
)
|
|
158
|
+
users = users.sort((a, b) => a._id.localeCompare(b._id))
|
|
159
|
+
await config.api.groups.updateGroupUsers(groupId, {
|
|
160
|
+
add: users.map(u => u._id),
|
|
161
|
+
remove: [],
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe("pagination", () => {
|
|
166
|
+
it("should return first page", async () => {
|
|
167
|
+
const result = await config.api.groups.searchUsers(groupId)
|
|
168
|
+
expect(result.body).toEqual({
|
|
169
|
+
users: users.slice(0, 10),
|
|
170
|
+
bookmark: users[10]._id,
|
|
171
|
+
hasNextPage: true,
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it("given a bookmark, should return skip items", async () => {
|
|
176
|
+
const result = await config.api.groups.searchUsers(groupId, {
|
|
177
|
+
bookmark: users[7]._id,
|
|
178
|
+
})
|
|
179
|
+
expect(result.body).toEqual({
|
|
180
|
+
users: users.slice(7, 17),
|
|
181
|
+
bookmark: users[17]._id,
|
|
182
|
+
hasNextPage: true,
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it("bookmarking the last page, should return last page info", async () => {
|
|
187
|
+
const result = await config.api.groups.searchUsers(groupId, {
|
|
188
|
+
bookmark: users[20]._id,
|
|
189
|
+
})
|
|
190
|
+
expect(result.body).toEqual({
|
|
191
|
+
users: users.slice(20),
|
|
192
|
+
bookmark: undefined,
|
|
193
|
+
hasNextPage: false,
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
describe("search by email", () => {
|
|
199
|
+
it('should be able to search "starting" by email', async () => {
|
|
200
|
+
const result = await config.api.groups.searchUsers(groupId, {
|
|
201
|
+
emailSearch: `user1`,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const matchedUsers = users
|
|
205
|
+
.filter(u => u.email.startsWith("user1"))
|
|
206
|
+
.sort((a, b) => a.email.localeCompare(b.email))
|
|
207
|
+
|
|
208
|
+
expect(result.body).toEqual({
|
|
209
|
+
users: matchedUsers.slice(0, 10),
|
|
210
|
+
bookmark: matchedUsers[10].email,
|
|
211
|
+
hasNextPage: true,
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it("should be able to bookmark when searching by email", async () => {
|
|
216
|
+
const matchedUsers = users
|
|
217
|
+
.filter(u => u.email.startsWith("user1"))
|
|
218
|
+
.sort((a, b) => a.email.localeCompare(b.email))
|
|
219
|
+
|
|
220
|
+
const result = await config.api.groups.searchUsers(groupId, {
|
|
221
|
+
emailSearch: `user1`,
|
|
222
|
+
bookmark: matchedUsers[4].email,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
expect(result.body).toEqual({
|
|
226
|
+
users: matchedUsers.slice(4),
|
|
227
|
+
bookmark: undefined,
|
|
228
|
+
hasNextPage: false,
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
})
|
|
58
234
|
})
|
package/src/tests/api/groups.ts
CHANGED
|
@@ -7,13 +7,13 @@ export class GroupsAPI extends TestAPI {
|
|
|
7
7
|
super(config)
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
saveGroup = (group: UserGroup) => {
|
|
10
|
+
saveGroup = (group: UserGroup, { expect } = { expect: 200 }) => {
|
|
11
11
|
return this.request
|
|
12
12
|
.post(`/api/global/groups`)
|
|
13
13
|
.send(group)
|
|
14
14
|
.set(this.config.defaultHeaders())
|
|
15
15
|
.expect("Content-Type", /json/)
|
|
16
|
-
.expect(
|
|
16
|
+
.expect(expect)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
deleteGroup = (id: string, rev: string) => {
|
|
@@ -23,4 +23,34 @@ export class GroupsAPI extends TestAPI {
|
|
|
23
23
|
.expect("Content-Type", /json/)
|
|
24
24
|
.expect(200)
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
searchUsers = (
|
|
28
|
+
id: string,
|
|
29
|
+
params?: { bookmark?: string; emailSearch?: string }
|
|
30
|
+
) => {
|
|
31
|
+
let url = `/api/global/groups/${id}/users?`
|
|
32
|
+
if (params?.bookmark) {
|
|
33
|
+
url += `bookmark=${params.bookmark}&`
|
|
34
|
+
}
|
|
35
|
+
if (params?.emailSearch) {
|
|
36
|
+
url += `emailSearch=${params.emailSearch}&`
|
|
37
|
+
}
|
|
38
|
+
return this.request
|
|
39
|
+
.get(url)
|
|
40
|
+
.set(this.config.defaultHeaders())
|
|
41
|
+
.expect("Content-Type", /json/)
|
|
42
|
+
.expect(200)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
updateGroupUsers = (
|
|
46
|
+
id: string,
|
|
47
|
+
body: { add: string[]; remove: string[] }
|
|
48
|
+
) => {
|
|
49
|
+
return this.request
|
|
50
|
+
.post(`/api/global/groups/${id}/users`)
|
|
51
|
+
.send(body)
|
|
52
|
+
.set(this.config.defaultHeaders())
|
|
53
|
+
.expect("Content-Type", /json/)
|
|
54
|
+
.expect(200)
|
|
55
|
+
}
|
|
26
56
|
}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
+
import { generator } from "@budibase/backend-core/tests"
|
|
2
|
+
import { db } from "@budibase/backend-core"
|
|
3
|
+
import { UserGroupRoles } from "@budibase/types"
|
|
4
|
+
|
|
1
5
|
export const UserGroup = () => {
|
|
6
|
+
const appsCount = generator.integer({ min: 0, max: 3 })
|
|
7
|
+
const roles = Array.from({ length: appsCount }).reduce(
|
|
8
|
+
(p: UserGroupRoles, v) => {
|
|
9
|
+
return {
|
|
10
|
+
...p,
|
|
11
|
+
[db.generateAppID()]: generator.pickone(["ADMIN", "POWER", "BASIC"]),
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{}
|
|
15
|
+
)
|
|
16
|
+
|
|
2
17
|
let group = {
|
|
3
18
|
apps: [],
|
|
4
|
-
color:
|
|
5
|
-
icon:
|
|
6
|
-
name:
|
|
7
|
-
roles:
|
|
19
|
+
color: generator.color(),
|
|
20
|
+
icon: generator.word(),
|
|
21
|
+
name: generator.word(),
|
|
22
|
+
roles: roles,
|
|
8
23
|
users: [],
|
|
9
24
|
}
|
|
10
25
|
return group
|
package/src/utilities/redis.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -9,18 +9,14 @@
|
|
|
9
9
|
"@budibase/types": ["../types/src"],
|
|
10
10
|
"@budibase/backend-core": ["../backend-core/src"],
|
|
11
11
|
"@budibase/backend-core/*": ["../backend-core/*"],
|
|
12
|
-
"@budibase/pro": ["
|
|
12
|
+
"@budibase/pro": ["../pro/packages/pro/src"]
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"ts-node": {
|
|
16
16
|
"require": ["tsconfig-paths/register"],
|
|
17
17
|
"swc": true
|
|
18
18
|
},
|
|
19
|
-
"references": [
|
|
20
|
-
{ "path": "../types" },
|
|
21
|
-
{ "path": "../backend-core" },
|
|
22
|
-
{ "path": "../../../budibase-pro/packages/pro" }
|
|
23
|
-
],
|
|
19
|
+
"references": [{ "path": "../types" }, { "path": "../backend-core" }],
|
|
24
20
|
"include": ["src/**/*"],
|
|
25
21
|
"exclude": ["dist"]
|
|
26
22
|
}
|