agentlang 0.0.2

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 (240) hide show
  1. package/LICENSE +83 -0
  2. package/README.md +120 -0
  3. package/bin/cli.js +4 -0
  4. package/out/api/http.d.ts +3 -0
  5. package/out/api/http.d.ts.map +1 -0
  6. package/out/api/http.js +290 -0
  7. package/out/api/http.js.map +1 -0
  8. package/out/cli/cli-util.d.ts +7 -0
  9. package/out/cli/cli-util.d.ts.map +1 -0
  10. package/out/cli/cli-util.js +9 -0
  11. package/out/cli/cli-util.js.map +1 -0
  12. package/out/cli/docs.d.ts +2 -0
  13. package/out/cli/docs.d.ts.map +1 -0
  14. package/out/cli/docs.js +236 -0
  15. package/out/cli/docs.js.map +1 -0
  16. package/out/cli/main.d.ts +288 -0
  17. package/out/cli/main.d.ts.map +1 -0
  18. package/out/cli/main.js +119 -0
  19. package/out/cli/main.js.map +1 -0
  20. package/out/cli/openapi-docs.yml +695 -0
  21. package/out/extension/main.cjs +18093 -0
  22. package/out/extension/main.cjs.map +7 -0
  23. package/out/extension/main.d.ts +4 -0
  24. package/out/extension/main.d.ts.map +1 -0
  25. package/out/extension/main.js +42 -0
  26. package/out/extension/main.js.map +1 -0
  27. package/out/language/agentlang-module.d.ts +42 -0
  28. package/out/language/agentlang-module.d.ts.map +1 -0
  29. package/out/language/agentlang-module.js +42 -0
  30. package/out/language/agentlang-module.js.map +1 -0
  31. package/out/language/agentlang-validator.d.ts +15 -0
  32. package/out/language/agentlang-validator.d.ts.map +1 -0
  33. package/out/language/agentlang-validator.js +50 -0
  34. package/out/language/agentlang-validator.js.map +1 -0
  35. package/out/language/generated/ast.d.ts +491 -0
  36. package/out/language/generated/ast.d.ts.map +1 -0
  37. package/out/language/generated/ast.js +934 -0
  38. package/out/language/generated/ast.js.map +1 -0
  39. package/out/language/generated/grammar.d.ts +7 -0
  40. package/out/language/generated/grammar.d.ts.map +1 -0
  41. package/out/language/generated/grammar.js +4475 -0
  42. package/out/language/generated/grammar.js.map +1 -0
  43. package/out/language/generated/module.d.ts +14 -0
  44. package/out/language/generated/module.d.ts.map +1 -0
  45. package/out/language/generated/module.js +21 -0
  46. package/out/language/generated/module.js.map +1 -0
  47. package/out/language/main-browser.d.ts +2 -0
  48. package/out/language/main-browser.d.ts.map +1 -0
  49. package/out/language/main-browser.js +10 -0
  50. package/out/language/main-browser.js.map +1 -0
  51. package/out/language/main.cjs +36229 -0
  52. package/out/language/main.cjs.map +7 -0
  53. package/out/language/main.d.ts +2 -0
  54. package/out/language/main.d.ts.map +1 -0
  55. package/out/language/main.js +11 -0
  56. package/out/language/main.js.map +1 -0
  57. package/out/language/parser.d.ts +9 -0
  58. package/out/language/parser.d.ts.map +1 -0
  59. package/out/language/parser.js +273 -0
  60. package/out/language/parser.js.map +1 -0
  61. package/out/language/syntax.d.ts +155 -0
  62. package/out/language/syntax.d.ts.map +1 -0
  63. package/out/language/syntax.js +527 -0
  64. package/out/language/syntax.js.map +1 -0
  65. package/out/runtime/agents/common.d.ts +2 -0
  66. package/out/runtime/agents/common.d.ts.map +1 -0
  67. package/out/runtime/agents/common.js +178 -0
  68. package/out/runtime/agents/common.js.map +1 -0
  69. package/out/runtime/agents/impl/openai.d.ts +8 -0
  70. package/out/runtime/agents/impl/openai.d.ts.map +1 -0
  71. package/out/runtime/agents/impl/openai.js +15 -0
  72. package/out/runtime/agents/impl/openai.js.map +1 -0
  73. package/out/runtime/agents/provider.d.ts +21 -0
  74. package/out/runtime/agents/provider.d.ts.map +1 -0
  75. package/out/runtime/agents/provider.js +32 -0
  76. package/out/runtime/agents/provider.js.map +1 -0
  77. package/out/runtime/agents/registry.d.ts +2 -0
  78. package/out/runtime/agents/registry.d.ts.map +1 -0
  79. package/out/runtime/agents/registry.js +10 -0
  80. package/out/runtime/agents/registry.js.map +1 -0
  81. package/out/runtime/auth/cognito.d.ts +16 -0
  82. package/out/runtime/auth/cognito.d.ts.map +1 -0
  83. package/out/runtime/auth/cognito.js +186 -0
  84. package/out/runtime/auth/cognito.js.map +1 -0
  85. package/out/runtime/auth/defs.d.ts +11 -0
  86. package/out/runtime/auth/defs.d.ts.map +1 -0
  87. package/out/runtime/auth/defs.js +24 -0
  88. package/out/runtime/auth/defs.js.map +1 -0
  89. package/out/runtime/auth/interface.d.ts +22 -0
  90. package/out/runtime/auth/interface.d.ts.map +1 -0
  91. package/out/runtime/auth/interface.js +2 -0
  92. package/out/runtime/auth/interface.js.map +1 -0
  93. package/out/runtime/defs.js +24 -0
  94. package/out/runtime/defs.js.map +1 -0
  95. package/out/runtime/interpreter.d.ts +69 -0
  96. package/out/runtime/interpreter.d.ts.map +1 -0
  97. package/out/runtime/interpreter.js +1163 -0
  98. package/out/runtime/interpreter.js.map +1 -0
  99. package/out/runtime/loader.d.ts +25 -0
  100. package/out/runtime/loader.d.ts.map +1 -0
  101. package/out/runtime/loader.js +346 -0
  102. package/out/runtime/loader.js.map +1 -0
  103. package/out/runtime/logger.d.ts +2 -0
  104. package/out/runtime/logger.d.ts.map +1 -0
  105. package/out/runtime/logger.js +44 -0
  106. package/out/runtime/logger.js.map +1 -0
  107. package/out/runtime/module.d.ts +273 -0
  108. package/out/runtime/module.d.ts.map +1 -0
  109. package/out/runtime/module.js +1786 -0
  110. package/out/runtime/module.js.map +1 -0
  111. package/out/runtime/modules/ai.d.ts +26 -0
  112. package/out/runtime/modules/ai.d.ts.map +1 -0
  113. package/out/runtime/modules/ai.js +211 -0
  114. package/out/runtime/modules/ai.js.map +1 -0
  115. package/out/runtime/modules/auth.d.ts +39 -0
  116. package/out/runtime/modules/auth.d.ts.map +1 -0
  117. package/out/runtime/modules/auth.js +359 -0
  118. package/out/runtime/modules/auth.js.map +1 -0
  119. package/out/runtime/modules/core.d.ts +2 -0
  120. package/out/runtime/modules/core.d.ts.map +1 -0
  121. package/out/runtime/modules/core.js +67 -0
  122. package/out/runtime/modules/core.js.map +1 -0
  123. package/out/runtime/relgraph.d.ts +21 -0
  124. package/out/runtime/relgraph.d.ts.map +1 -0
  125. package/out/runtime/relgraph.js +156 -0
  126. package/out/runtime/relgraph.js.map +1 -0
  127. package/out/runtime/resolvers/interface.d.ts +59 -0
  128. package/out/runtime/resolvers/interface.d.ts.map +1 -0
  129. package/out/runtime/resolvers/interface.js +111 -0
  130. package/out/runtime/resolvers/interface.js.map +1 -0
  131. package/out/runtime/resolvers/registry.d.ts +8 -0
  132. package/out/runtime/resolvers/registry.d.ts.map +1 -0
  133. package/out/runtime/resolvers/registry.js +26 -0
  134. package/out/runtime/resolvers/registry.js.map +1 -0
  135. package/out/runtime/resolvers/sqldb/database.d.ts +50 -0
  136. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -0
  137. package/out/runtime/resolvers/sqldb/database.js +618 -0
  138. package/out/runtime/resolvers/sqldb/database.js.map +1 -0
  139. package/out/runtime/resolvers/sqldb/dbutil.d.ts +18 -0
  140. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -0
  141. package/out/runtime/resolvers/sqldb/dbutil.js +221 -0
  142. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -0
  143. package/out/runtime/resolvers/sqldb/impl.d.ts +26 -0
  144. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -0
  145. package/out/runtime/resolvers/sqldb/impl.js +300 -0
  146. package/out/runtime/resolvers/sqldb/impl.js.map +1 -0
  147. package/out/runtime/state.js +83 -0
  148. package/out/runtime/state.js.map +1 -0
  149. package/out/runtime/util.d.ts +43 -0
  150. package/out/runtime/util.d.ts.map +1 -0
  151. package/out/runtime/util.js +447 -0
  152. package/out/runtime/util.js.map +1 -0
  153. package/out/setupClassic.d.ts +98 -0
  154. package/out/setupClassic.d.ts.map +1 -0
  155. package/out/setupClassic.js +38 -0
  156. package/out/setupClassic.js.map +1 -0
  157. package/out/setupCommon.d.ts +2 -0
  158. package/out/setupCommon.d.ts.map +1 -0
  159. package/out/setupCommon.js +33 -0
  160. package/out/setupCommon.js.map +1 -0
  161. package/out/setupExtended.d.ts +40 -0
  162. package/out/setupExtended.d.ts.map +1 -0
  163. package/out/setupExtended.js +67 -0
  164. package/out/setupExtended.js.map +1 -0
  165. package/out/syntaxes/agentlang.monarch.d.ts +77 -0
  166. package/out/syntaxes/agentlang.monarch.d.ts.map +1 -0
  167. package/out/syntaxes/agentlang.monarch.js +31 -0
  168. package/out/syntaxes/agentlang.monarch.js.map +1 -0
  169. package/out/utils/fs/index.d.ts +14 -0
  170. package/out/utils/fs/index.d.ts.map +1 -0
  171. package/out/utils/fs/index.js +26 -0
  172. package/out/utils/fs/index.js.map +1 -0
  173. package/out/utils/fs/interfaces.d.ts +105 -0
  174. package/out/utils/fs/interfaces.d.ts.map +1 -0
  175. package/out/utils/fs/interfaces.js +5 -0
  176. package/out/utils/fs/interfaces.js.map +1 -0
  177. package/out/utils/fs/lightning-fs.d.ts +116 -0
  178. package/out/utils/fs/lightning-fs.d.ts.map +1 -0
  179. package/out/utils/fs/lightning-fs.js +243 -0
  180. package/out/utils/fs/lightning-fs.js.map +1 -0
  181. package/out/utils/fs/node-fs.d.ts +93 -0
  182. package/out/utils/fs/node-fs.d.ts.map +1 -0
  183. package/out/utils/fs/node-fs.js +169 -0
  184. package/out/utils/fs/node-fs.js.map +1 -0
  185. package/out/utils/fs-utils.d.ts +153 -0
  186. package/out/utils/fs-utils.d.ts.map +1 -0
  187. package/out/utils/fs-utils.js +271 -0
  188. package/out/utils/fs-utils.js.map +1 -0
  189. package/out/utils/runtime.d.ts +36 -0
  190. package/out/utils/runtime.d.ts.map +1 -0
  191. package/out/utils/runtime.js +39 -0
  192. package/out/utils/runtime.js.map +1 -0
  193. package/package.json +155 -0
  194. package/src/api/http.ts +361 -0
  195. package/src/cli/cli-util.ts +18 -0
  196. package/src/cli/main.ts +146 -0
  197. package/src/extension/main.ts +51 -0
  198. package/src/language/agentlang-module.ts +75 -0
  199. package/src/language/agentlang-validator.ts +60 -0
  200. package/src/language/agentlang.langium +178 -0
  201. package/src/language/generated/ast.ts +1698 -0
  202. package/src/language/generated/grammar.ts +4477 -0
  203. package/src/language/generated/module.ts +25 -0
  204. package/src/language/main-browser.ts +19 -0
  205. package/src/language/main.ts +13 -0
  206. package/src/language/parser.ts +329 -0
  207. package/src/language/syntax.ts +646 -0
  208. package/src/runtime/agents/common.ts +177 -0
  209. package/src/runtime/agents/impl/openai.ts +19 -0
  210. package/src/runtime/agents/provider.ts +58 -0
  211. package/src/runtime/agents/registry.ts +9 -0
  212. package/src/runtime/auth/cognito.ts +225 -0
  213. package/src/runtime/auth/defs.ts +33 -0
  214. package/src/runtime/auth/interface.ts +31 -0
  215. package/src/runtime/defs.ts +33 -0
  216. package/src/runtime/interpreter.ts +1352 -0
  217. package/src/runtime/loader.ts +450 -0
  218. package/src/runtime/logger.ts +51 -0
  219. package/src/runtime/module.ts +2188 -0
  220. package/src/runtime/modules/ai.ts +257 -0
  221. package/src/runtime/modules/auth.ts +489 -0
  222. package/src/runtime/modules/core.ts +95 -0
  223. package/src/runtime/relgraph.ts +195 -0
  224. package/src/runtime/resolvers/interface.ts +160 -0
  225. package/src/runtime/resolvers/registry.ts +30 -0
  226. package/src/runtime/resolvers/sqldb/database.ts +823 -0
  227. package/src/runtime/resolvers/sqldb/dbutil.ts +257 -0
  228. package/src/runtime/resolvers/sqldb/impl.ts +471 -0
  229. package/src/runtime/state.ts +87 -0
  230. package/src/runtime/util.ts +513 -0
  231. package/src/setupClassic.ts +43 -0
  232. package/src/setupCommon.ts +33 -0
  233. package/src/setupExtended.ts +79 -0
  234. package/src/syntaxes/agentlang.monarch.ts +31 -0
  235. package/src/utils/fs/index.ts +28 -0
  236. package/src/utils/fs/interfaces.ts +118 -0
  237. package/src/utils/fs/lightning-fs.ts +284 -0
  238. package/src/utils/fs/node-fs.ts +185 -0
  239. package/src/utils/fs-utils.ts +304 -0
  240. package/src/utils/runtime.ts +43 -0
@@ -0,0 +1,823 @@
1
+ import { DataSource, EntityManager, EntitySchema, QueryRunner, SelectQueryBuilder } from 'typeorm';
2
+ import { logger } from '../../logger.js';
3
+ import {
4
+ DefaultVectorDimension,
5
+ modulesAsOrmSchema,
6
+ OwnersSuffix,
7
+ VectorSuffix,
8
+ } from './dbutil.js';
9
+ import { DefaultAuthInfo, ResolverAuthInfo } from '../interface.js';
10
+ import { canUserCreate, canUserDelete, canUserRead, canUserUpdate } from '../../modules/auth.js';
11
+ import { Environment, GlobalEnvironment } from '../../interpreter.js';
12
+ import {
13
+ Instance,
14
+ InstanceAttributes,
15
+ newInstanceAttributes,
16
+ RbacPermissionFlag,
17
+ Relationship,
18
+ } from '../../module.js';
19
+ import pgvector from 'pgvector';
20
+ import { isString } from '../../util.js';
21
+ import { DeletedFlagAttributeName, PathAttributeName, UnauthorisedError } from '../../defs.js';
22
+
23
+ export let defaultDataSource: DataSource | undefined;
24
+
25
+ export class DbContext {
26
+ txnId: string | undefined;
27
+ authInfo: ResolverAuthInfo;
28
+ private inKernelMode: boolean = false;
29
+ resourceFqName: string;
30
+ activeEnv: Environment;
31
+ private needAuthCheckFlag: boolean = true;
32
+
33
+ constructor(
34
+ resourceFqName: string,
35
+ authInfo: ResolverAuthInfo,
36
+ activeEnv: Environment,
37
+ txnId?: string,
38
+ inKernelMode?: boolean
39
+ ) {
40
+ this.resourceFqName = resourceFqName;
41
+ this.authInfo = authInfo;
42
+ this.activeEnv = activeEnv;
43
+ this.txnId = txnId;
44
+ if (inKernelMode != undefined) {
45
+ this.inKernelMode = inKernelMode;
46
+ }
47
+ }
48
+ private static GlobalDbContext: DbContext | undefined;
49
+
50
+ static getGlobalContext(): DbContext {
51
+ if (DbContext.GlobalDbContext == undefined) {
52
+ DbContext.GlobalDbContext = new DbContext(
53
+ '',
54
+ DefaultAuthInfo,
55
+ GlobalEnvironment,
56
+ undefined,
57
+ true
58
+ );
59
+ }
60
+ return DbContext.GlobalDbContext;
61
+ }
62
+
63
+ // Shallow clone
64
+ clone(): DbContext {
65
+ return new DbContext(
66
+ this.resourceFqName,
67
+ this.authInfo,
68
+ this.activeEnv,
69
+ this.txnId,
70
+ this.inKernelMode
71
+ );
72
+ }
73
+
74
+ getUserId(): string {
75
+ return this.authInfo.userId;
76
+ }
77
+
78
+ isForDelete(): boolean {
79
+ return this.authInfo.readForDelete;
80
+ }
81
+
82
+ isForUpdate(): boolean {
83
+ return this.authInfo.readForUpdate;
84
+ }
85
+
86
+ setResourceFqNameFrom(inst: Instance): DbContext {
87
+ this.resourceFqName = inst.getFqName();
88
+ return this;
89
+ }
90
+
91
+ setNeedAuthCheck(flag: boolean): DbContext {
92
+ this.needAuthCheckFlag = flag;
93
+ return this;
94
+ }
95
+
96
+ isPermitted(): boolean {
97
+ return this.inKernelMode || !this.needAuthCheckFlag;
98
+ }
99
+
100
+ isInKernelMode(): boolean {
101
+ return this.inKernelMode;
102
+ }
103
+ }
104
+
105
+ export type JoinOn = {
106
+ attributeName: string;
107
+ operator: string;
108
+ attributeValue: any;
109
+ };
110
+
111
+ export function makeJoinOn(attrName: string, attrValue: any, opr: string = '='): JoinOn {
112
+ return {
113
+ attributeName: attrName,
114
+ attributeValue: attrValue,
115
+ operator: opr,
116
+ };
117
+ }
118
+
119
+ export type JoinClause = {
120
+ tableName: string;
121
+ queryObject?: object;
122
+ queryValues?: object;
123
+ joinOn: JoinOn | JoinOn[];
124
+ };
125
+
126
+ export type DatabaseConfig = {
127
+ type: string;
128
+ host?: string;
129
+ username?: string;
130
+ password?: string;
131
+ dbname?: string;
132
+ port?: number;
133
+ };
134
+
135
+ function mkDbName(): string {
136
+ return process.env.AGENTLANG_DB_NAME || `db-${Date.now()}`;
137
+ }
138
+
139
+ function makePostgresDataSource(
140
+ entities: EntitySchema[],
141
+ config: DatabaseConfig | undefined,
142
+ synchronize: boolean = true
143
+ ): DataSource {
144
+ return new DataSource({
145
+ type: 'postgres',
146
+ host: process.env.POSTGRES_HOST || config?.host || 'localhost',
147
+ port: getPostgressEnvPort() || config?.port || 5432,
148
+ username: process.env.POSTGRES_USER || config?.username || 'postgres',
149
+ password: process.env.POSTGRES_PASSWORD || config?.password || 'postgres',
150
+ database: process.env.POSTGRES_DB || config?.dbname || 'postgres',
151
+ synchronize: synchronize,
152
+ entities: entities,
153
+ });
154
+ }
155
+
156
+ function getPostgressEnvPort(): number | undefined {
157
+ const s: string | undefined = process.env.POSTGRES_PORT;
158
+ if (s) {
159
+ return Number(s);
160
+ } else {
161
+ return undefined;
162
+ }
163
+ }
164
+
165
+ function makeSqliteDataSource(
166
+ entities: EntitySchema[],
167
+ config: DatabaseConfig | undefined,
168
+ synchronize: boolean = true
169
+ ): DataSource {
170
+ return new DataSource({
171
+ type: 'sqlite',
172
+ database: config?.dbname || mkDbName(),
173
+ synchronize: synchronize,
174
+ entities: entities,
175
+ });
176
+ }
177
+
178
+ export const DbType = 'sqlite';
179
+
180
+ function getDsFunction(
181
+ config: DatabaseConfig | undefined
182
+ ): (
183
+ entities: EntitySchema<any>[],
184
+ config: DatabaseConfig | undefined,
185
+ synchronize?: boolean | undefined
186
+ ) => DataSource {
187
+ switch (process.env.AL_DB_TYPE || config?.type || DbType) {
188
+ case 'sqlite':
189
+ return makeSqliteDataSource;
190
+ case 'postgres':
191
+ return makePostgresDataSource;
192
+ default:
193
+ throw new Error(`Unsupported database type - ${config?.type}`);
194
+ }
195
+ }
196
+
197
+ export async function initDatabase(config: DatabaseConfig | undefined) {
198
+ if (defaultDataSource == undefined) {
199
+ const mkds = getDsFunction(config);
200
+ if (mkds) {
201
+ const ormScm = modulesAsOrmSchema();
202
+ defaultDataSource = mkds(ormScm.entities, config) as DataSource;
203
+ await defaultDataSource.initialize();
204
+ const vectEnts = ormScm.vectorEntities.map((es: EntitySchema) => {
205
+ return es.options.name;
206
+ });
207
+ if (vectEnts.length > 0) {
208
+ await initVectorStore(vectEnts, DbContext.getGlobalContext());
209
+ }
210
+ } else {
211
+ throw new Error(`Unsupported database type - ${DbType}`);
212
+ }
213
+ }
214
+ }
215
+
216
+ export async function resetDefaultDatabase() {
217
+ if (defaultDataSource) {
218
+ await defaultDataSource.destroy();
219
+ defaultDataSource = undefined;
220
+ }
221
+ }
222
+
223
+ function ownersTable(tableName: string): string {
224
+ return (tableName + OwnersSuffix).toLowerCase();
225
+ }
226
+
227
+ async function insertRowsHelper(
228
+ tableName: string,
229
+ rows: object[],
230
+ ctx: DbContext,
231
+ doUpsert: boolean
232
+ ): Promise<void> {
233
+ const repo = getDatasourceForTransaction(ctx.txnId).getRepository(tableName);
234
+ if (doUpsert) await repo.save(rows);
235
+ else await repo.insert(rows);
236
+ }
237
+
238
+ export async function addRowForFullTextSearch(
239
+ tableName: string,
240
+ id: string,
241
+ vect: number[],
242
+ ctx: DbContext
243
+ ) {
244
+ try {
245
+ const vecTableName = tableName + VectorSuffix;
246
+ const qb = getDatasourceForTransaction(ctx.txnId).createQueryBuilder();
247
+ await qb
248
+ .insert()
249
+ .into(vecTableName)
250
+ .values([{ id: id, embedding: pgvector.toSql(vect) }])
251
+ .execute();
252
+ } catch (err: any) {
253
+ logger.error(`Failed to add row to vector store - ${err}`);
254
+ }
255
+ }
256
+
257
+ export async function initVectorStore(tableNames: string[], ctx: DbContext) {
258
+ let notInited = true;
259
+ tableNames.forEach(async (vecTableName: string) => {
260
+ const vecRepo = getDatasourceForTransaction(ctx.txnId).getRepository(vecTableName);
261
+ if (notInited) {
262
+ let failure = false;
263
+ try {
264
+ await vecRepo.query('CREATE EXTENSION IF NOT EXISTS vector');
265
+ } catch (err: any) {
266
+ logger.error(`Failed to initialize vector store - ${err}`);
267
+ failure = true;
268
+ }
269
+ if (failure) return;
270
+ notInited = false;
271
+ }
272
+ await vecRepo.query(
273
+ `CREATE TABLE IF NOT EXISTS ${vecTableName} (
274
+ id varchar PRIMARY KEY,
275
+ embedding vector(${DefaultVectorDimension}),
276
+ __is_deleted__ boolean default false
277
+ )`
278
+ );
279
+ });
280
+ }
281
+
282
+ export async function vectorStoreSearch(
283
+ tableName: string,
284
+ searchVec: number[],
285
+ limit: number,
286
+ ctx: DbContext
287
+ ): Promise<any> {
288
+ try {
289
+ const vecTableName = tableName + VectorSuffix;
290
+ const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
291
+ return await qb.query(
292
+ `select id from ${vecTableName} order by embedding <-> $1 LIMIT ${limit}`,
293
+ [pgvector.toSql(searchVec)]
294
+ );
295
+ } catch (err: any) {
296
+ logger.error(`Vector store search failed - ${err}`);
297
+ }
298
+ }
299
+
300
+ export async function vectorStoreSearchEntryExists(
301
+ tableName: string,
302
+ id: string,
303
+ ctx: DbContext
304
+ ): Promise<boolean> {
305
+ try {
306
+ const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
307
+ const vecTableName = tableName + VectorSuffix;
308
+ const result: any[] = await qb.query(`select id from ${vecTableName} where id = $1`, [id]);
309
+ return result != null && result.length > 0;
310
+ } catch (err: any) {
311
+ logger.error(`Vector store search failed - ${err}`);
312
+ }
313
+ return false;
314
+ }
315
+
316
+ export async function deleteFullTextSearchEntry(tableName: string, id: string, ctx: DbContext) {
317
+ try {
318
+ const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
319
+ const vecTableName = tableName + VectorSuffix;
320
+ await qb.query(`delete from ${vecTableName} where id = $1`, [id]);
321
+ } catch (err: any) {
322
+ logger.error(`Vector store delete failed - ${err}`);
323
+ }
324
+ }
325
+
326
+ async function checkUserPerm(
327
+ opr: RbacPermissionFlag,
328
+ ctx: DbContext,
329
+ instRows: object
330
+ ): Promise<boolean> {
331
+ let hasPerm = ctx.isPermitted();
332
+ if (!hasPerm) {
333
+ const userId = ctx.getUserId();
334
+ let f: Function | undefined;
335
+ switch (opr) {
336
+ case RbacPermissionFlag.CREATE:
337
+ f = canUserCreate;
338
+ break;
339
+ case RbacPermissionFlag.READ:
340
+ f = canUserRead;
341
+ break;
342
+ case RbacPermissionFlag.UPDATE:
343
+ f = canUserUpdate;
344
+ break;
345
+ case RbacPermissionFlag.DELETE:
346
+ f = canUserDelete;
347
+ break;
348
+ default:
349
+ f = undefined;
350
+ }
351
+ if (f != undefined) {
352
+ hasPerm = await f(userId, ctx.resourceFqName, ctx.activeEnv);
353
+ }
354
+ }
355
+ if (!hasPerm) {
356
+ hasPerm = await isOwnerOfParent(instRows[PathKey], ctx);
357
+ }
358
+ return hasPerm;
359
+ }
360
+
361
+ async function checkCreatePermission(ctx: DbContext, inst: Instance): Promise<boolean> {
362
+ const tmpCtx = ctx.clone().setResourceFqNameFrom(inst);
363
+ return await checkUserPerm(
364
+ RbacPermissionFlag.CREATE,
365
+ tmpCtx,
366
+ Object.fromEntries(inst.attributes)
367
+ );
368
+ }
369
+
370
+ export async function insertRows(
371
+ tableName: string,
372
+ rows: object[],
373
+ ctx: DbContext,
374
+ doUpsert: boolean = false
375
+ ): Promise<void> {
376
+ let hasPerm = ctx.isPermitted();
377
+ if (!hasPerm) {
378
+ hasPerm = await checkUserPerm(RbacPermissionFlag.CREATE, ctx, rows[0]);
379
+ }
380
+ if (hasPerm) {
381
+ await insertRowsHelper(tableName, rows, ctx, doUpsert);
382
+ if (!ctx.isInKernelMode() && !doUpsert) {
383
+ await createOwnership(tableName, rows, ctx);
384
+ }
385
+ } else {
386
+ throw new UnauthorisedError({ opr: 'insert', entity: tableName });
387
+ }
388
+ }
389
+
390
+ export async function insertRow(
391
+ tableName: string,
392
+ row: object,
393
+ ctx: DbContext,
394
+ doUpsert: boolean
395
+ ): Promise<void> {
396
+ const rows: Array<object> = new Array<object>();
397
+ rows.push(row);
398
+ await insertRows(tableName, rows, ctx, doUpsert);
399
+ }
400
+
401
+ export async function insertBetweenRow(
402
+ n: string,
403
+ a1: string,
404
+ a2: string,
405
+ node1: Instance,
406
+ node2: Instance,
407
+ relEntry: Relationship,
408
+ ctx: DbContext
409
+ ): Promise<void> {
410
+ let hasPerm = await checkCreatePermission(ctx, node1);
411
+ if (hasPerm) {
412
+ hasPerm = await checkCreatePermission(ctx, node2);
413
+ }
414
+ if (hasPerm) {
415
+ const attrs: InstanceAttributes = newInstanceAttributes();
416
+ const p1 = node1.attributes.get(PathAttributeName);
417
+ const p2 = node2.attributes.get(PathAttributeName);
418
+ attrs.set(a1, p1);
419
+ attrs.set(a2, p2);
420
+ attrs.set(PathAttributeName, crypto.randomUUID());
421
+ if (relEntry.isOneToMany()) {
422
+ attrs.set(relEntry.joinNodesAttributeName(), `${p1}_${p2}`);
423
+ }
424
+ const row = Object.fromEntries(attrs);
425
+ await insertRow(n, row, ctx.clone().setNeedAuthCheck(false), false);
426
+ } else {
427
+ throw new UnauthorisedError({ opr: 'insert', entity: n });
428
+ }
429
+ }
430
+
431
+ const PathKey = PathAttributeName as keyof object;
432
+
433
+ async function createOwnership(tableName: string, rows: object[], ctx: DbContext): Promise<void> {
434
+ const ownerRows: object[] = [];
435
+ rows.forEach((r: object) => {
436
+ ownerRows.push({
437
+ id: crypto.randomUUID(),
438
+ path: r[PathKey],
439
+ user_id: ctx.authInfo.userId,
440
+ });
441
+ });
442
+ const tname = ownersTable(tableName);
443
+ await insertRowsHelper(tname, ownerRows, ctx, false);
444
+ }
445
+
446
+ async function isOwnerOfParent(path: string, ctx: DbContext): Promise<boolean> {
447
+ const parts = path.split('/');
448
+ if (parts.length <= 2) {
449
+ return false;
450
+ }
451
+ const parentPaths = new Array<[string, string]>();
452
+ let i = 0;
453
+ let lastPath: string | undefined;
454
+ while (i < parts.length - 2) {
455
+ const parentName = parts[i].replace('$', '_');
456
+ const parentPath = `${lastPath ? lastPath + '/' : ''}${parts[i]}/${parts[i + 1]}`;
457
+ lastPath = `${parentPath}/${parts[i + 2]}`;
458
+ parentPaths.push([parentName, parentPath]);
459
+ i += 3;
460
+ }
461
+ if (parentPaths.length == 0) {
462
+ return false;
463
+ }
464
+ for (let i = 0; i < parentPaths.length; ++i) {
465
+ const [parentName, parentPath] = parentPaths[i];
466
+ const result = await isOwner(parentName, parentPath, ctx);
467
+ if (result) return result;
468
+ }
469
+ return false;
470
+ }
471
+
472
+ async function isOwner(parentName: string, instPath: string, ctx: DbContext): Promise<boolean> {
473
+ const userId = ctx.getUserId();
474
+ const tabName = ownersTable(parentName);
475
+ const alias = tabName;
476
+ const query = [
477
+ `${alias}.path = '${instPath}'`,
478
+ `${alias}.user_id = '${userId}'`,
479
+ `${alias}.type = 'o'`,
480
+ ];
481
+ let result: any = undefined;
482
+ const sq: SelectQueryBuilder<any> = getDatasourceForTransaction(ctx.txnId)
483
+ .createQueryBuilder()
484
+ .select()
485
+ .from(tabName, alias)
486
+ .where(query.join(' AND '));
487
+ try {
488
+ await sq.getRawMany().then((r: any) => (result = r));
489
+ } catch (reason: any) {
490
+ logger.error(`Failed to check ownership on parent ${parentName} - ${reason}`);
491
+ }
492
+ if (result == undefined || result.length == 0) {
493
+ return false;
494
+ }
495
+ return true;
496
+ }
497
+
498
+ export async function upsertRows(tableName: string, rows: object[], ctx: DbContext): Promise<void> {
499
+ await insertRows(tableName, rows, ctx, true);
500
+ }
501
+
502
+ export async function upsertRow(tableName: string, row: object, ctx: DbContext): Promise<void> {
503
+ const rows: Array<object> = new Array<object>();
504
+ rows.push(row);
505
+ await upsertRows(tableName, rows, ctx);
506
+ }
507
+
508
+ export async function updateRow(
509
+ tableName: string,
510
+ queryObj: object,
511
+ queryVals: object,
512
+ updateObj: object,
513
+ ctx: DbContext
514
+ ): Promise<boolean> {
515
+ await getDatasourceForTransaction(ctx.txnId)
516
+ .createQueryBuilder()
517
+ .update(tableName)
518
+ .set(updateObj)
519
+ .where(objectToWhereClause(queryObj, queryVals), queryVals)
520
+ .execute();
521
+ return true;
522
+ }
523
+
524
+ type QueryObjectEntry = [string, any];
525
+ export type QueryObject = Array<QueryObjectEntry>;
526
+
527
+ function queryObjectAsWhereClause(qobj: QueryObject): string {
528
+ const ss: Array<string> = [];
529
+ qobj.forEach((kv: QueryObjectEntry) => {
530
+ const k = kv[0];
531
+ ss.push(`${k} = :${k}`);
532
+ });
533
+ return ss.join(' AND ');
534
+ }
535
+
536
+ export async function hardDeleteRow(tableName: string, queryObject: QueryObject, ctx: DbContext) {
537
+ const clause = queryObjectAsWhereClause(queryObject);
538
+ await getDatasourceForTransaction(ctx.txnId)
539
+ .createQueryBuilder()
540
+ .delete()
541
+ .from(tableName)
542
+ .where(clause, Object.fromEntries(queryObject))
543
+ .execute();
544
+ return true;
545
+ }
546
+
547
+ function mkBetweenClause(tableName: string | undefined, k: string, queryVals: any): string {
548
+ const ov = queryVals[k];
549
+ if (ov instanceof Array) {
550
+ const isstr = isString(ov[0]);
551
+ const v1 = isstr ? `'${ov[0]}'` : ov[0];
552
+ const v2 = isstr ? `'${ov[1]}'` : ov[1];
553
+ const s = tableName
554
+ ? `${tableName}.${k} BETWEEN ${v1} AND ${v2}`
555
+ : `${k} BETWEEN ${v1} AND ${v2}`;
556
+ delete queryVals[k];
557
+ return s;
558
+ } else {
559
+ throw new Error(`between requires an array argument, not ${ov}`);
560
+ }
561
+ }
562
+
563
+ function objectToWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
564
+ const clauses: Array<string> = new Array<string>();
565
+ Object.entries(queryObj).forEach((value: [string, any]) => {
566
+ const op: string = value[1] as string;
567
+ const clause =
568
+ op == 'between'
569
+ ? mkBetweenClause(tableName, value[0], queryVals)
570
+ : tableName
571
+ ? `${tableName}.${value[0]} ${op} :${value[0]}`
572
+ : `${value[0]} ${op} :${value[0]}`;
573
+ clauses.push(clause);
574
+ });
575
+ return clauses.join(' AND ');
576
+ }
577
+
578
+ function objectToRawWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
579
+ const clauses: Array<string> = new Array<string>();
580
+ Object.entries(queryObj).forEach((value: [string, any]) => {
581
+ const op: string = value[1] as string;
582
+ const k: string = value[0];
583
+ let clause = '';
584
+ if (op == 'between') {
585
+ clause = mkBetweenClause(tableName, k, queryVals);
586
+ } else {
587
+ const ov: any = queryVals[k];
588
+ const v = isString(ov) ? `'${ov}'` : ov;
589
+ clause = tableName ? `${tableName}.${k} ${op} ${v}` : `${k} ${op} ${v}`;
590
+ }
591
+ clauses.push(clause);
592
+ });
593
+ if (clauses.length > 0) {
594
+ return clauses.join(' AND ');
595
+ } else {
596
+ return '';
597
+ }
598
+ }
599
+
600
+ export async function getMany(
601
+ tableName: string,
602
+ queryObj: object | undefined,
603
+ queryVals: object | undefined,
604
+ ctx: DbContext
605
+ ): Promise<any> {
606
+ const alias: string = tableName.toLowerCase();
607
+ const queryStr: string = withNotDeletedClause(
608
+ alias,
609
+ queryObj != undefined ? objectToWhereClause(queryObj, queryVals, alias) : ''
610
+ );
611
+ let ownersJoinCond: string[] | undefined;
612
+ let ot: string = '';
613
+ let otAlias: string = '';
614
+ if (!ctx.isPermitted()) {
615
+ const userId = ctx.getUserId();
616
+ const fqName = ctx.resourceFqName;
617
+ const env: Environment = ctx.activeEnv;
618
+ let hasGlobalPerms = await canUserRead(userId, fqName, env);
619
+ if (hasGlobalPerms) {
620
+ if (ctx.isForUpdate()) {
621
+ hasGlobalPerms = await canUserUpdate(userId, fqName, env);
622
+ } else if (ctx.isForDelete()) {
623
+ hasGlobalPerms = await canUserDelete(userId, fqName, env);
624
+ }
625
+ }
626
+ if (!hasGlobalPerms) {
627
+ ot = ownersTable(tableName);
628
+ otAlias = ot.toLowerCase();
629
+ ownersJoinCond = [
630
+ `${otAlias}.path = ${alias}.${PathAttributeName}`,
631
+ `${otAlias}.user_id = '${ctx.authInfo.userId}'`,
632
+ `${otAlias}.r = true`,
633
+ ];
634
+ if (ctx.isForUpdate()) {
635
+ ownersJoinCond.push(`${otAlias}.u = true`);
636
+ }
637
+ if (ctx.isForDelete()) {
638
+ ownersJoinCond.push(`${otAlias}.d = true`);
639
+ }
640
+ }
641
+ }
642
+
643
+ const qb: SelectQueryBuilder<any> = getDatasourceForTransaction(ctx.txnId)
644
+ .getRepository(tableName)
645
+ .createQueryBuilder();
646
+ if (ownersJoinCond) {
647
+ qb.innerJoin(ot, otAlias, ownersJoinCond.join(' AND '));
648
+ }
649
+ qb.where(queryStr, queryVals);
650
+ return await qb.getMany();
651
+ }
652
+
653
+ export async function getManyByJoin(
654
+ tableName: string,
655
+ queryObj: object | undefined,
656
+ queryVals: object | undefined,
657
+ joinClauses: JoinClause[],
658
+ intoSpec: Map<string, string>,
659
+ ctx: DbContext
660
+ ): Promise<any> {
661
+ const alias: string = tableName.toLowerCase();
662
+ const queryStr: string = withNotDeletedClause(
663
+ alias,
664
+ queryObj != undefined ? objectToRawWhereClause(queryObj, queryVals, alias) : ''
665
+ );
666
+ let ot: string = '';
667
+ let otAlias: string = '';
668
+ if (!ctx.isPermitted()) {
669
+ const userId = ctx.getUserId();
670
+ const fqName = ctx.resourceFqName;
671
+ const env: Environment = ctx.activeEnv;
672
+ const hasGlobalPerms = await canUserRead(userId, fqName, env);
673
+ if (!hasGlobalPerms) {
674
+ ot = ownersTable(tableName);
675
+ otAlias = ot.toLowerCase();
676
+ joinClauses.push({
677
+ tableName: otAlias,
678
+ joinOn: [
679
+ makeJoinOn(`${otAlias}.path`, `${alias}.${PathAttributeName}`),
680
+ makeJoinOn(`${otAlias}.user_id`, `'${ctx.authInfo.userId}'`),
681
+ makeJoinOn(`${otAlias}.r`, true),
682
+ ],
683
+ });
684
+ }
685
+ }
686
+ const joinSql = new Array<string>();
687
+ joinClauses.forEach((jc: JoinClause) => {
688
+ joinSql.push(
689
+ `inner join ${jc.tableName} as ${jc.tableName} on ${joinOnAsSql(jc.joinOn)} AND ${jc.tableName}.${DeletedFlagAttributeName} = false`
690
+ );
691
+ if (jc.queryObject) {
692
+ const q = objectToRawWhereClause(jc.queryObject, jc.queryValues, jc.tableName);
693
+ if (q.length > 0) {
694
+ joinSql.push(` AND ${q}`);
695
+ }
696
+ }
697
+ });
698
+ const sql = `SELECT ${intoSpecToSql(intoSpec)} FROM ${tableName} ${joinSql.join('\n')} WHERE ${queryStr}`;
699
+ logger.debug(`Join Query: ${sql}`);
700
+ const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
701
+ return await qb.query(sql);
702
+ }
703
+
704
+ function intoSpecToSql(intoSpec: Map<string, string>): string {
705
+ const cols = new Array<string>();
706
+ intoSpec.forEach((v: string, k: string) => {
707
+ cols.push(`${v} AS ${k}`);
708
+ });
709
+ return cols.join(', ');
710
+ }
711
+
712
+ function joinOnAsSql(joinOn: JoinOn | JoinOn[]): string {
713
+ if (joinOn instanceof Array) {
714
+ return joinOn.map(joinOnAsSql).join(' AND ');
715
+ } else {
716
+ return `${joinOn.attributeName} ${joinOn.operator} ${joinOn.attributeValue}`;
717
+ }
718
+ }
719
+
720
+ function notDeletedClause(alias: string): string {
721
+ return `${alias}.${DeletedFlagAttributeName} = false`;
722
+ }
723
+
724
+ function withNotDeletedClause(alias: string, sql: string): string {
725
+ if (sql == '') {
726
+ return notDeletedClause(alias);
727
+ } else {
728
+ return `${sql} AND ${notDeletedClause(alias)}`;
729
+ }
730
+ }
731
+
732
+ export type BetweenConnectionInfo = {
733
+ connectionTable: string;
734
+ fromColumn: string;
735
+ fromValue: string;
736
+ toColumn: string;
737
+ toRef: string;
738
+ };
739
+
740
+ function buildQueryFromConnnectionInfo(
741
+ connAlias: string,
742
+ mainAlias: string,
743
+ connInfo: BetweenConnectionInfo
744
+ ): string {
745
+ return `${connAlias}.${connInfo.fromColumn} = ${connInfo.fromValue} AND ${connAlias}.${connInfo.toColumn} = ${mainAlias}.${connInfo.toRef}`;
746
+ }
747
+
748
+ export async function getAllConnected(
749
+ tableName: string,
750
+ queryObj: object,
751
+ queryVals: object,
752
+ connInfo: BetweenConnectionInfo,
753
+ ctx: DbContext
754
+ ) {
755
+ const alias: string = tableName.toLowerCase();
756
+ const connAlias: string = connInfo.connectionTable.toLowerCase();
757
+ const qb = getDatasourceForTransaction(ctx.txnId)
758
+ .createQueryBuilder()
759
+ .select()
760
+ .from(tableName, alias)
761
+ .where(objectToWhereClause(queryObj, alias), queryVals)
762
+ .innerJoin(
763
+ connInfo.connectionTable,
764
+ connAlias,
765
+ buildQueryFromConnnectionInfo(connAlias, alias, connInfo)
766
+ );
767
+ return await qb.getRawMany();
768
+ }
769
+
770
+ const transactionsDb: Map<string, QueryRunner> = new Map<string, QueryRunner>();
771
+
772
+ export async function startDbTransaction(): Promise<string> {
773
+ if (defaultDataSource != undefined) {
774
+ const queryRunner = defaultDataSource.createQueryRunner();
775
+ await queryRunner.startTransaction();
776
+ const txnId: string = crypto.randomUUID();
777
+ transactionsDb.set(txnId, queryRunner);
778
+ return txnId;
779
+ } else {
780
+ throw new Error('Database not initialized');
781
+ }
782
+ }
783
+
784
+ function getDatasourceForTransaction(txnId: string | undefined): DataSource | EntityManager {
785
+ if (txnId) {
786
+ const qr: QueryRunner | undefined = transactionsDb.get(txnId);
787
+ if (qr == undefined) {
788
+ throw new Error(`Transaction not found - ${txnId}`);
789
+ } else {
790
+ return qr.manager;
791
+ }
792
+ } else {
793
+ if (defaultDataSource != undefined) return defaultDataSource;
794
+ else throw new Error('No default datasource is initialized');
795
+ }
796
+ }
797
+
798
+ export async function commitDbTransaction(txnId: string): Promise<void> {
799
+ await endTransaction(txnId, true);
800
+ }
801
+
802
+ export async function rollbackDbTransaction(txnId: string): Promise<void> {
803
+ await endTransaction(txnId, false);
804
+ }
805
+
806
+ async function endTransaction(txnId: string, commit: boolean): Promise<void> {
807
+ const qr: QueryRunner | undefined = transactionsDb.get(txnId);
808
+ if (qr && qr.isTransactionActive) {
809
+ try {
810
+ if (commit)
811
+ await qr.commitTransaction().catch((reason: any) => {
812
+ logger.error(`failed to commit transaction ${txnId} - ${reason}`);
813
+ });
814
+ else
815
+ await qr.rollbackTransaction().catch((reason: any) => {
816
+ logger.error(`failed to rollback transaction ${txnId} - ${reason}`);
817
+ });
818
+ } finally {
819
+ await qr.release();
820
+ transactionsDb.delete(txnId);
821
+ }
822
+ }
823
+ }