@exulu/backend 1.46.0 → 1.47.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 (151) hide show
  1. package/.agents/skills/mintlify/SKILL.md +347 -0
  2. package/.editorconfig +15 -0
  3. package/.eslintrc.json +52 -0
  4. package/.jscpd.json +18 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc.json +12 -0
  7. package/CHANGELOG.md +11 -4
  8. package/README.md +747 -0
  9. package/SECURITY.md +5 -0
  10. package/dist/index.cjs +12015 -10506
  11. package/dist/index.d.cts +725 -667
  12. package/dist/index.d.ts +725 -667
  13. package/dist/index.js +12034 -10518
  14. package/ee/LICENSE.md +62 -0
  15. package/ee/agentic-retrieval/index.ts +1109 -0
  16. package/ee/documents/THIRD_PARTY_LICENSES/docling.txt +31 -0
  17. package/ee/documents/processing/build_pdf_processor.sh +35 -0
  18. package/ee/documents/processing/chunk_markdown.py +263 -0
  19. package/ee/documents/processing/doc_processor.ts +635 -0
  20. package/ee/documents/processing/pdf_processor.spec +115 -0
  21. package/ee/documents/processing/pdf_to_markdown.py +420 -0
  22. package/ee/documents/processing/requirements.txt +4 -0
  23. package/ee/entitlements.ts +49 -0
  24. package/ee/markdown.ts +686 -0
  25. package/ee/queues/decorator.ts +140 -0
  26. package/ee/queues/queues.ts +156 -0
  27. package/ee/queues/server.ts +6 -0
  28. package/ee/rbac-resolver.ts +51 -0
  29. package/ee/rbac-update.ts +111 -0
  30. package/ee/schemas.ts +347 -0
  31. package/ee/tokenizer.ts +80 -0
  32. package/ee/workers.ts +1423 -0
  33. package/eslint.config.js +88 -0
  34. package/jest.config.ts +25 -0
  35. package/license.md +73 -49
  36. package/mintlify-docs/.mintignore +7 -0
  37. package/mintlify-docs/AGENTS.md +33 -0
  38. package/mintlify-docs/CLAUDE.MD +50 -0
  39. package/mintlify-docs/CONTRIBUTING.md +32 -0
  40. package/mintlify-docs/LICENSE +21 -0
  41. package/mintlify-docs/README.md +55 -0
  42. package/mintlify-docs/ai-tools/claude-code.mdx +43 -0
  43. package/mintlify-docs/ai-tools/cursor.mdx +39 -0
  44. package/mintlify-docs/ai-tools/windsurf.mdx +39 -0
  45. package/mintlify-docs/api-reference/core-types/agent-types.mdx +110 -0
  46. package/mintlify-docs/api-reference/core-types/analytics-types.mdx +95 -0
  47. package/mintlify-docs/api-reference/core-types/configuration-types.mdx +83 -0
  48. package/mintlify-docs/api-reference/core-types/evaluation-types.mdx +106 -0
  49. package/mintlify-docs/api-reference/core-types/job-types.mdx +135 -0
  50. package/mintlify-docs/api-reference/core-types/overview.mdx +73 -0
  51. package/mintlify-docs/api-reference/core-types/prompt-types.mdx +102 -0
  52. package/mintlify-docs/api-reference/core-types/rbac-types.mdx +163 -0
  53. package/mintlify-docs/api-reference/core-types/session-types.mdx +77 -0
  54. package/mintlify-docs/api-reference/core-types/user-management.mdx +112 -0
  55. package/mintlify-docs/api-reference/core-types/workflow-types.mdx +88 -0
  56. package/mintlify-docs/api-reference/core-types.mdx +585 -0
  57. package/mintlify-docs/api-reference/dynamic-types.mdx +851 -0
  58. package/mintlify-docs/api-reference/endpoint/create.mdx +4 -0
  59. package/mintlify-docs/api-reference/endpoint/delete.mdx +4 -0
  60. package/mintlify-docs/api-reference/endpoint/get.mdx +4 -0
  61. package/mintlify-docs/api-reference/endpoint/webhook.mdx +4 -0
  62. package/mintlify-docs/api-reference/introduction.mdx +661 -0
  63. package/mintlify-docs/api-reference/mutations.mdx +1012 -0
  64. package/mintlify-docs/api-reference/openapi.json +217 -0
  65. package/mintlify-docs/api-reference/queries.mdx +1154 -0
  66. package/mintlify-docs/backend/introduction.mdx +218 -0
  67. package/mintlify-docs/changelog.mdx +293 -0
  68. package/mintlify-docs/community-edition.mdx +304 -0
  69. package/mintlify-docs/core/exulu-agent/api-reference.mdx +894 -0
  70. package/mintlify-docs/core/exulu-agent/configuration.mdx +690 -0
  71. package/mintlify-docs/core/exulu-agent/introduction.mdx +552 -0
  72. package/mintlify-docs/core/exulu-app/api-reference.mdx +481 -0
  73. package/mintlify-docs/core/exulu-app/configuration.mdx +319 -0
  74. package/mintlify-docs/core/exulu-app/introduction.mdx +117 -0
  75. package/mintlify-docs/core/exulu-authentication.mdx +810 -0
  76. package/mintlify-docs/core/exulu-chunkers/api-reference.mdx +1011 -0
  77. package/mintlify-docs/core/exulu-chunkers/configuration.mdx +596 -0
  78. package/mintlify-docs/core/exulu-chunkers/introduction.mdx +403 -0
  79. package/mintlify-docs/core/exulu-context/api-reference.mdx +911 -0
  80. package/mintlify-docs/core/exulu-context/configuration.mdx +648 -0
  81. package/mintlify-docs/core/exulu-context/introduction.mdx +394 -0
  82. package/mintlify-docs/core/exulu-database.mdx +811 -0
  83. package/mintlify-docs/core/exulu-default-agents.mdx +545 -0
  84. package/mintlify-docs/core/exulu-eval/api-reference.mdx +772 -0
  85. package/mintlify-docs/core/exulu-eval/configuration.mdx +680 -0
  86. package/mintlify-docs/core/exulu-eval/introduction.mdx +459 -0
  87. package/mintlify-docs/core/exulu-logging.mdx +464 -0
  88. package/mintlify-docs/core/exulu-otel.mdx +670 -0
  89. package/mintlify-docs/core/exulu-queues/api-reference.mdx +648 -0
  90. package/mintlify-docs/core/exulu-queues/configuration.mdx +650 -0
  91. package/mintlify-docs/core/exulu-queues/introduction.mdx +474 -0
  92. package/mintlify-docs/core/exulu-reranker/api-reference.mdx +630 -0
  93. package/mintlify-docs/core/exulu-reranker/configuration.mdx +663 -0
  94. package/mintlify-docs/core/exulu-reranker/introduction.mdx +516 -0
  95. package/mintlify-docs/core/exulu-tool/api-reference.mdx +723 -0
  96. package/mintlify-docs/core/exulu-tool/configuration.mdx +805 -0
  97. package/mintlify-docs/core/exulu-tool/introduction.mdx +539 -0
  98. package/mintlify-docs/core/exulu-variables/api-reference.mdx +699 -0
  99. package/mintlify-docs/core/exulu-variables/configuration.mdx +736 -0
  100. package/mintlify-docs/core/exulu-variables/introduction.mdx +511 -0
  101. package/mintlify-docs/development.mdx +94 -0
  102. package/mintlify-docs/docs.json +248 -0
  103. package/mintlify-docs/enterprise-edition.mdx +538 -0
  104. package/mintlify-docs/essentials/code.mdx +35 -0
  105. package/mintlify-docs/essentials/images.mdx +59 -0
  106. package/mintlify-docs/essentials/markdown.mdx +88 -0
  107. package/mintlify-docs/essentials/navigation.mdx +87 -0
  108. package/mintlify-docs/essentials/reusable-snippets.mdx +110 -0
  109. package/mintlify-docs/essentials/settings.mdx +318 -0
  110. package/mintlify-docs/favicon.svg +3 -0
  111. package/mintlify-docs/frontend/introduction.mdx +39 -0
  112. package/mintlify-docs/getting-started.mdx +267 -0
  113. package/mintlify-docs/guides/custom-agent.mdx +608 -0
  114. package/mintlify-docs/guides/first-agent.mdx +315 -0
  115. package/mintlify-docs/images/admin_ui.png +0 -0
  116. package/mintlify-docs/images/contexts.png +0 -0
  117. package/mintlify-docs/images/create_agents.png +0 -0
  118. package/mintlify-docs/images/evals.png +0 -0
  119. package/mintlify-docs/images/graphql.png +0 -0
  120. package/mintlify-docs/images/graphql_api.png +0 -0
  121. package/mintlify-docs/images/hero-dark.png +0 -0
  122. package/mintlify-docs/images/hero-light.png +0 -0
  123. package/mintlify-docs/images/hero.png +0 -0
  124. package/mintlify-docs/images/knowledge_sources.png +0 -0
  125. package/mintlify-docs/images/mcp.png +0 -0
  126. package/mintlify-docs/images/scaling.png +0 -0
  127. package/mintlify-docs/index.mdx +411 -0
  128. package/mintlify-docs/logo/dark.svg +9 -0
  129. package/mintlify-docs/logo/light.svg +9 -0
  130. package/mintlify-docs/partners.mdx +558 -0
  131. package/mintlify-docs/products.mdx +77 -0
  132. package/mintlify-docs/snippets/snippet-intro.mdx +4 -0
  133. package/mintlify-docs/styles.css +207 -0
  134. package/{documentation → old-documentation}/logging.md +3 -3
  135. package/package.json +35 -4
  136. package/skills-lock.json +10 -0
  137. package/types/context-processor.ts +45 -0
  138. package/types/exulu-table-definition.ts +79 -0
  139. package/types/file-types.ts +18 -0
  140. package/types/models/agent.ts +10 -12
  141. package/types/models/exulu-agent-tool-config.ts +11 -0
  142. package/types/models/rate-limiter-rules.ts +7 -0
  143. package/types/provider-config.ts +21 -0
  144. package/types/queue-config.ts +16 -0
  145. package/types/rbac-rights-modes.ts +1 -0
  146. package/types/statistics.ts +20 -0
  147. package/types/workflow.ts +31 -0
  148. package/changelog-backend-10.11.2025_03.12.2025.md +0 -316
  149. package/types/models/agent-backend.ts +0 -15
  150. /package/{documentation → old-documentation}/otel.md +0 -0
  151. /package/{patch-older-releases-readme.md → old-documentation/patch-older-releases.md} +0 -0
@@ -0,0 +1,140 @@
1
+ import { Queue } from "bullmq";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import type { UIMessage } from "ai";
4
+ import type { STATISTICS_LABELS } from "@EXULU_TYPES/statistics";
5
+
6
+ type ExuluJobType = "embedder" | "workflow" | "eval" | "processor";
7
+
8
+ type ExuluBullMqDecoratorData = {
9
+ queue: Queue;
10
+ label: string;
11
+ embedder?: string;
12
+ processor?: string;
13
+ inputs: any;
14
+ user?: number;
15
+ role?: string;
16
+ trigger: STATISTICS_LABELS;
17
+ workflow?: string;
18
+ evaluation?: string;
19
+ item?: string;
20
+ context?: string;
21
+ retries?: number;
22
+ backoff?: {
23
+ type: "exponential" | "linear";
24
+ delay: number; // in milliseconds
25
+ };
26
+ timeoutInSeconds: number;
27
+ };
28
+
29
+ export type BullMqJobData = {
30
+ label: string;
31
+ type: string;
32
+ source?: string;
33
+ inputs: any;
34
+ timeoutInSeconds: number;
35
+ user?: number;
36
+ role?: string;
37
+ trigger: STATISTICS_LABELS;
38
+ messages?: UIMessage[];
39
+ eval_run_id?: string;
40
+ eval_run_name?: string;
41
+ test_case_id?: string;
42
+ test_case_name?: string;
43
+ eval_functions?: {
44
+ id: string;
45
+ config: Record<string, any>;
46
+ }[];
47
+ agent_id?: string;
48
+ expected_output?: string;
49
+ expected_tools?: string[];
50
+ expected_knowledge_sources?: string[];
51
+ expected_agent_tools?: string[];
52
+ config?: Record<string, any>;
53
+ scoring_method?: string;
54
+ pass_threshold?: number;
55
+ workflow?: string;
56
+ embedder?: string;
57
+ processor?: string;
58
+ evaluation?: string;
59
+ item?: string;
60
+ context?: string;
61
+ };
62
+
63
+ export const bullmqDecorator = async ({
64
+ queue,
65
+ label,
66
+ embedder,
67
+ processor,
68
+ inputs,
69
+ evaluation,
70
+ user,
71
+ role,
72
+ trigger,
73
+ workflow,
74
+ item,
75
+ context,
76
+ retries,
77
+ backoff,
78
+ timeoutInSeconds,
79
+ }: ExuluBullMqDecoratorData) => {
80
+ const types = [embedder, workflow, processor, evaluation];
81
+
82
+ if (types.filter((type) => type).length > 1) {
83
+ throw new Error(
84
+ "Cannot have multiple types in the same job, must be one of the following: embedder, workflow, processor or eval.",
85
+ );
86
+ }
87
+
88
+ let type: ExuluJobType = "embedder";
89
+
90
+ if (workflow) {
91
+ type = "workflow";
92
+ }
93
+
94
+ if (processor) {
95
+ type = "processor";
96
+ }
97
+
98
+ if (evaluation) {
99
+ type = "eval";
100
+ }
101
+
102
+ if (embedder) {
103
+ type = "embedder";
104
+ }
105
+
106
+ const jobData: BullMqJobData = {
107
+ label,
108
+ type: `${type}`,
109
+ timeoutInSeconds: timeoutInSeconds || 180, // 3 minutes default
110
+ inputs,
111
+ ...(user && { user }),
112
+ ...(role && { role }),
113
+ ...(trigger && { trigger }),
114
+ ...(workflow && { workflow }),
115
+ ...(embedder && { embedder }),
116
+ ...(processor && { processor }),
117
+ ...(evaluation && { evaluation }),
118
+ ...(item && { item }),
119
+ ...(context && { context }),
120
+ };
121
+
122
+ const redisId = uuidv4();
123
+ const job = await queue.add(`${embedder || workflow || processor || evaluation}`, jobData, {
124
+ jobId: redisId,
125
+ // Setting it to 3 as a sensible default, as
126
+ // many AI services are quite unstable.
127
+ attempts: retries || 3, // todo make this configurable?
128
+ removeOnComplete: 5000,
129
+ removeOnFail: 10000,
130
+ backoff: backoff || {
131
+ type: "exponential",
132
+ delay: 2000,
133
+ },
134
+ });
135
+
136
+ return {
137
+ ...job,
138
+ redis: job.id,
139
+ };
140
+ };
@@ -0,0 +1,156 @@
1
+ import { Queue } from "bullmq";
2
+ import { redisServer } from "./server";
3
+ import { BullMQOtel } from "bullmq-otel";
4
+ import type { ExuluQueueConfig } from "@EXULU_TYPES/queue-config";
5
+ import { checkLicense } from "@EE/entitlements";
6
+
7
+ // Used for workflows and embedders
8
+ class ExuluQueues {
9
+ queues: {
10
+ queue: Queue;
11
+ ratelimit: number;
12
+ concurrency: {
13
+ worker: number;
14
+ queue: number;
15
+ };
16
+ timeoutInSeconds: number;
17
+ }[];
18
+ constructor() {
19
+ this.queues = [];
20
+ }
21
+
22
+ public list: Map<
23
+ string,
24
+ {
25
+ name: string;
26
+ concurrency: {
27
+ worker: number;
28
+ queue: number;
29
+ };
30
+ ratelimit: number;
31
+ timeoutInSeconds: number;
32
+ use: () => Promise<ExuluQueueConfig>;
33
+ }
34
+ > = new Map(); // list of queue names
35
+
36
+ queue(name: string):
37
+ | {
38
+ queue: Queue;
39
+ ratelimit: number;
40
+ concurrency: {
41
+ worker: number;
42
+ queue: number;
43
+ };
44
+ }
45
+ | undefined {
46
+ return this.queues.find((x) => x.queue?.name === name) as
47
+ | {
48
+ queue: Queue;
49
+ ratelimit: number;
50
+ concurrency: {
51
+ worker: number;
52
+ queue: number;
53
+ };
54
+ }
55
+ | undefined;
56
+ }
57
+
58
+ // name: string
59
+ // concurrency: global concurrency for the queue
60
+ // ratelimit: maximum number of jobs per second
61
+ // Rate limit is set on workers (see workers.ts), even global rate limits,
62
+ // that is a bit counter-intuitive. Since queues are registered using .user
63
+ // method of ExuluQueues we need to store the desired rate limit on the queue
64
+ // here so we can use the value when creating workers for the queue instance
65
+ // as there is no way to store a rate limit value natively on a bullm queue.
66
+ register = (
67
+ name: string,
68
+ concurrency: {
69
+ worker: number;
70
+ queue: number;
71
+ },
72
+ ratelimit: number = 1,
73
+ timeoutInSeconds: number = 180,
74
+ ): {
75
+ use: () => Promise<ExuluQueueConfig>;
76
+ } => {
77
+ const license = checkLicense();
78
+ if (!license["queues"]) {
79
+ throw new Error(`[EXULU] You are not licensed to use queues so cannot register a queue. Please set your EXULU_ENTERPRISE_LICENSE env variable.`);
80
+ }
81
+ const queueConcurrency = concurrency.queue || 1;
82
+ const workerConcurrency = concurrency.worker || 1;
83
+
84
+ const use = async (): Promise<ExuluQueueConfig> => {
85
+ const existing = this.queues.find((x) => x.queue?.name === name);
86
+ if (existing) {
87
+ const globalConcurrency = await existing.queue.getGlobalConcurrency();
88
+ if (globalConcurrency !== queueConcurrency) {
89
+ await existing.queue.setGlobalConcurrency(queueConcurrency);
90
+ }
91
+ return {
92
+ queue: existing.queue,
93
+ ratelimit,
94
+ concurrency: {
95
+ worker: workerConcurrency,
96
+ queue: queueConcurrency,
97
+ },
98
+ timeoutInSeconds,
99
+ };
100
+ }
101
+
102
+ if (!redisServer.host?.length || !redisServer.port?.length) {
103
+ console.error(
104
+ `[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or embedder (look for ExuluQueues.register().use() ).`,
105
+ );
106
+ console.error("Stack trace:");
107
+ console.trace();
108
+ throw new Error(`[EXULU] no redis server configured.`);
109
+ }
110
+
111
+ const newQueue = new Queue(`${name}`, {
112
+ connection: {
113
+ ...redisServer,
114
+ enableOfflineQueue: false,
115
+ },
116
+ telemetry: new BullMQOtel("simple-guide"),
117
+ });
118
+ await newQueue.setGlobalConcurrency(queueConcurrency);
119
+ this.queues.push({
120
+ queue: newQueue,
121
+ ratelimit,
122
+ concurrency: {
123
+ worker: workerConcurrency,
124
+ queue: queueConcurrency,
125
+ },
126
+ timeoutInSeconds,
127
+ });
128
+ return {
129
+ queue: newQueue,
130
+ ratelimit,
131
+ concurrency: {
132
+ worker: workerConcurrency,
133
+ queue: queueConcurrency,
134
+ },
135
+ timeoutInSeconds,
136
+ };
137
+ };
138
+
139
+ this.list.set(name, {
140
+ name,
141
+ concurrency: {
142
+ worker: workerConcurrency,
143
+ queue: queueConcurrency,
144
+ },
145
+ ratelimit,
146
+ timeoutInSeconds,
147
+ use,
148
+ });
149
+
150
+ return {
151
+ use,
152
+ };
153
+ };
154
+ }
155
+
156
+ export const queues = new ExuluQueues();
@@ -0,0 +1,6 @@
1
+ export const redisServer = {
2
+ host: process.env.REDIS_HOST ?? "",
3
+ port: (process.env.REDIS_PORT as any) ?? "",
4
+ password: process.env.REDIS_PASSWORD || undefined,
5
+ username: process.env.REDIS_USER || "",
6
+ };
@@ -0,0 +1,51 @@
1
+ import { checkLicense } from "./entitlements";
2
+
3
+ export const RBACResolver = async (
4
+ db: any,
5
+ entityName: string,
6
+ resourceId: string,
7
+ rights_mode: string,
8
+ ): Promise<{
9
+ type: string;
10
+ users: any[];
11
+ roles: any[];
12
+ }> => {
13
+
14
+ // If RBAC is not available
15
+ // the system defaults to public.
16
+ const license = checkLicense()
17
+ if (!license.rbac) {
18
+ return {
19
+ type: "public",
20
+ users: [],
21
+ roles: []
22
+ }
23
+ }
24
+ // Get RBAC records for this resource
25
+ const rbacRecords = await db
26
+ .from("rbac")
27
+ .where({
28
+ entity: entityName,
29
+ target_resource_id: resourceId,
30
+ })
31
+ .select("*");
32
+
33
+ const users = rbacRecords
34
+ .filter((r) => r.access_type === "User")
35
+ ?.map((r) => ({ id: r.user_id, rights: r.rights }));
36
+
37
+ const roles = rbacRecords
38
+ .filter((r) => r.access_type === "Role")
39
+ ?.map((r) => ({ id: r.role_id, rights: r.rights }));
40
+
41
+ // Determine the type based on rights_mode or presence of records
42
+ let type = rights_mode || "private";
43
+ if (type === "users" && users.length === 0) type = "private";
44
+ if (type === "roles" && roles.length === 0) type = "private";
45
+
46
+ return {
47
+ type,
48
+ users,
49
+ roles,
50
+ };
51
+ };
@@ -0,0 +1,111 @@
1
+ // Helper function to handle RBAC updates
2
+ import { checkLicense } from "./entitlements.ts";
3
+
4
+ export const handleRBACUpdate = async (
5
+ db: any,
6
+ entityName: string,
7
+ resourceId: string,
8
+ rbacData: any,
9
+ existingRbacRecords: any[],
10
+ ): Promise<void> => {
11
+
12
+ const license = checkLicense()
13
+ if (!license.rbac) {
14
+ console.warn(`[EXULU] You are not licensed to use RBAC.`);
15
+ return;
16
+ }
17
+
18
+ const { users = [], roles = [] /* projects = [] */ } = rbacData;
19
+
20
+ // Get existing RBAC records if not provided
21
+ if (!existingRbacRecords) {
22
+ existingRbacRecords = await db
23
+ .from("rbac")
24
+ .where({
25
+ entity: entityName,
26
+ target_resource_id: resourceId,
27
+ })
28
+ .select("*");
29
+ }
30
+
31
+ // Create sets for comparison
32
+ const newUserRecords = new Set(users.map((u: any) => `${u.id}:${u.rights}`));
33
+ const newRoleRecords = new Set(roles.map((r: any) => `${r.id}:${r.rights}`));
34
+ // const newProjectRecords = new Set(projects.map((p: any) => `${p.id}:${p.rights}`));
35
+ const existingUserRecords = new Set(
36
+ existingRbacRecords
37
+ .filter((r) => r.access_type === "User")
38
+ .map((r) => `${r.user_id}:${r.rights}`),
39
+ );
40
+ const existingRoleRecords = new Set(
41
+ existingRbacRecords
42
+ .filter((r) => r.access_type === "Role")
43
+ .map((r) => `${r.role_id}:${r.rights}`),
44
+ );
45
+
46
+ // Records to create
47
+ const usersToCreate = users.filter((u: any) => !existingUserRecords.has(`${u.id}:${u.rights}`));
48
+ const rolesToCreate = roles.filter((r: any) => !existingRoleRecords.has(`${r.id}:${r.rights}`));
49
+ // const projectsToCreate = projects.filter((p: any) => !existingProjectRecords.has(`${p.id}:${p.rights}`));
50
+
51
+ // Records to remove
52
+ const usersToRemove = existingRbacRecords.filter(
53
+ (r) => r.access_type === "User" && !newUserRecords.has(`${r.user_id}:${r.rights}`),
54
+ );
55
+ const rolesToRemove = existingRbacRecords.filter(
56
+ (r) => r.access_type === "Role" && !newRoleRecords.has(`${r.role_id}:${r.rights}`),
57
+ );
58
+ // const projectsToRemove = existingRbacRecords
59
+ // .filter(r => r.access_type === 'Project' && !newProjectRecords.has(`${r.project_id}:${r.rights}`));
60
+
61
+ // Remove obsolete records
62
+ if (usersToRemove.length > 0) {
63
+ await db
64
+ .from("rbac")
65
+ .whereIn(
66
+ "id",
67
+ usersToRemove.map((r) => r.id),
68
+ )
69
+ .del();
70
+ }
71
+ if (rolesToRemove.length > 0) {
72
+ await db
73
+ .from("rbac")
74
+ .whereIn(
75
+ "id",
76
+ rolesToRemove.map((r) => r.id),
77
+ )
78
+ .del();
79
+ }
80
+
81
+ // Create new records
82
+ const recordsToInsert: any[] = [];
83
+
84
+ usersToCreate.forEach((user: any) => {
85
+ recordsToInsert.push({
86
+ entity: entityName,
87
+ access_type: "User",
88
+ target_resource_id: resourceId,
89
+ user_id: user.id,
90
+ rights: user.rights,
91
+ createdAt: new Date(),
92
+ updatedAt: new Date(),
93
+ });
94
+ });
95
+
96
+ rolesToCreate.forEach((role: any) => {
97
+ recordsToInsert.push({
98
+ entity: entityName,
99
+ access_type: "Role",
100
+ target_resource_id: resourceId,
101
+ role_id: role.id,
102
+ rights: role.rights,
103
+ createdAt: new Date(),
104
+ updatedAt: new Date(),
105
+ });
106
+ });
107
+
108
+ if (recordsToInsert.length > 0) {
109
+ await db.from("rbac").insert(recordsToInsert);
110
+ }
111
+ };