agentlang 0.0.3 → 0.0.4
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/README.md +16 -47
- package/out/api/http.d.ts.map +1 -1
- package/out/api/http.js +12 -0
- package/out/api/http.js.map +1 -1
- package/out/cli/main.d.ts +1 -3
- package/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +6 -12
- package/out/cli/main.js.map +1 -1
- package/out/language/generated/ast.d.ts +80 -18
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +119 -26
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +831 -238
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/generated/module.d.ts +1 -1
- package/out/language/generated/module.js +1 -1
- package/out/language/main.cjs +995 -331
- package/out/language/main.cjs.map +2 -2
- package/out/language/parser.js +13 -6
- package/out/language/parser.js.map +1 -1
- package/out/runtime/agents/common.d.ts +1 -1
- package/out/runtime/agents/common.d.ts.map +1 -1
- package/out/runtime/agents/common.js +1 -1
- package/out/runtime/auth/cognito.d.ts +4 -1
- package/out/runtime/auth/cognito.d.ts.map +1 -1
- package/out/runtime/auth/cognito.js +540 -73
- package/out/runtime/auth/cognito.js.map +1 -1
- package/out/runtime/auth/defs.d.ts +3 -0
- package/out/runtime/auth/defs.d.ts.map +1 -1
- package/out/runtime/auth/defs.js +17 -1
- package/out/runtime/auth/defs.js.map +1 -1
- package/out/runtime/auth/interface.d.ts +6 -1
- package/out/runtime/auth/interface.d.ts.map +1 -1
- package/out/runtime/defs.d.ts +21 -0
- package/out/runtime/defs.d.ts.map +1 -1
- package/out/runtime/defs.js +35 -0
- package/out/runtime/defs.js.map +1 -1
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +45 -36
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/loader.d.ts +4 -2
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +148 -29
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/module.d.ts +48 -5
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +200 -9
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +7 -5
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +50 -24
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/auth.d.ts +17 -1
- package/out/runtime/modules/auth.d.ts.map +1 -1
- package/out/runtime/modules/auth.js +282 -30
- package/out/runtime/modules/auth.js.map +1 -1
- package/out/runtime/modules/core.d.ts.map +1 -1
- package/out/runtime/modules/core.js +3 -1
- package/out/runtime/modules/core.js.map +1 -1
- package/out/runtime/relgraph.d.ts.map +1 -1
- package/out/runtime/relgraph.js +2 -2
- package/out/runtime/relgraph.js.map +1 -1
- package/out/runtime/resolvers/interface.d.ts +37 -2
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js +103 -5
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/registry.d.ts +3 -2
- package/out/runtime/resolvers/registry.d.ts.map +1 -1
- package/out/runtime/resolvers/registry.js +3 -0
- package/out/runtime/resolvers/registry.js.map +1 -1
- package/out/runtime/state.d.ts +31 -3
- package/out/runtime/state.d.ts.map +1 -1
- package/out/runtime/state.js +11 -1
- package/out/runtime/state.js.map +1 -1
- package/out/runtime/util.d.ts +4 -0
- package/out/runtime/util.d.ts.map +1 -1
- package/out/runtime/util.js +16 -0
- package/out/runtime/util.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +2 -2
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/out/utils/http.d.ts +2 -0
- package/out/utils/http.d.ts.map +1 -0
- package/out/utils/http.js +5 -0
- package/out/utils/http.js.map +1 -0
- package/package.json +8 -6
- package/src/api/http.ts +15 -0
- package/src/cli/main.ts +6 -12
- package/src/language/agentlang.langium +31 -10
- package/src/language/generated/ast.ts +212 -44
- package/src/language/generated/grammar.ts +831 -238
- package/src/language/generated/module.ts +1 -1
- package/src/language/parser.ts +12 -8
- package/src/runtime/agents/common.ts +1 -1
- package/src/runtime/auth/cognito.ts +605 -74
- package/src/runtime/auth/defs.ts +17 -1
- package/src/runtime/auth/interface.ts +6 -1
- package/src/runtime/defs.ts +45 -0
- package/src/runtime/interpreter.ts +43 -34
- package/src/runtime/loader.ts +159 -30
- package/src/runtime/module.ts +243 -10
- package/src/runtime/modules/ai.ts +52 -28
- package/src/runtime/modules/auth.ts +330 -38
- package/src/runtime/modules/core.ts +3 -1
- package/src/runtime/relgraph.ts +2 -8
- package/src/runtime/resolvers/interface.ts +141 -6
- package/src/runtime/resolvers/registry.ts +5 -2
- package/src/runtime/state.ts +11 -1
- package/src/runtime/util.ts +17 -0
- package/src/syntaxes/agentlang.monarch.ts +2 -2
- package/src/utils/http.ts +5 -0
- package/src/index.ts +0 -29
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import { Result, Environment, makeEventEvaluator } from '../interpreter.js';
|
|
2
2
|
import { logger } from '../logger.js';
|
|
3
|
-
import { Instance, RbacPermissionFlag } from '../module.js';
|
|
3
|
+
import { Instance, makeInstance, newInstanceAttributes, RbacPermissionFlag } from '../module.js';
|
|
4
4
|
import { makeCoreModuleName } from '../util.js';
|
|
5
5
|
import { isSqlTrue } from '../resolvers/sqldb/dbutil.js';
|
|
6
6
|
import { AgentlangAuth, SessionInfo, UserInfo } from '../auth/interface.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ActiveSessionInfo,
|
|
9
|
+
AdminUserId,
|
|
10
|
+
BypassSession,
|
|
11
|
+
isAuthEnabled,
|
|
12
|
+
isRbacEnabled,
|
|
13
|
+
} from '../auth/defs.js';
|
|
8
14
|
import { isNodeEnv } from '../../utils/runtime.js';
|
|
9
|
-
import { CognitoAuth } from '../auth/cognito.js';
|
|
15
|
+
import { CognitoAuth, getHttpStatusForError } from '../auth/cognito.js';
|
|
16
|
+
import {
|
|
17
|
+
UnauthorisedError,
|
|
18
|
+
UserNotFoundError,
|
|
19
|
+
UserNotConfirmedError,
|
|
20
|
+
PasswordResetRequiredError,
|
|
21
|
+
TooManyRequestsError,
|
|
22
|
+
InvalidParameterError,
|
|
23
|
+
ExpiredCodeError,
|
|
24
|
+
CodeMismatchError,
|
|
25
|
+
BadRequestError,
|
|
26
|
+
} from '../defs.js';
|
|
10
27
|
|
|
11
28
|
export const CoreAuthModuleName = makeCoreModuleName('auth');
|
|
12
29
|
|
|
@@ -56,7 +73,7 @@ entity Permission {
|
|
|
56
73
|
relationship RolePermission between(Role, Permission)
|
|
57
74
|
|
|
58
75
|
workflow CreateRole {
|
|
59
|
-
|
|
76
|
+
{Role {name CreateRole.name}, @upsert}
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
workflow FindRole {
|
|
@@ -67,13 +84,13 @@ workflow FindRole {
|
|
|
67
84
|
workflow AssignUserToRole {
|
|
68
85
|
{User {id? AssignUserToRole.userId}} as [user];
|
|
69
86
|
{Role {name? AssignUserToRole.roleName}} as [role];
|
|
70
|
-
|
|
87
|
+
{UserRole {User user, Role role}, @upsert}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
workflow AssignUserToRoleByEmail {
|
|
74
91
|
{User {email? AssignUserToRoleByEmail.email}} as [user];
|
|
75
92
|
{Role {name? AssignUserToRoleByEmail.roleName}} as [role];
|
|
76
|
-
|
|
93
|
+
{UserRole {User user, Role role}, @upsert}
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
workflow FindUserRoles {
|
|
@@ -82,19 +99,20 @@ workflow FindUserRoles {
|
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
workflow CreatePermission {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
{Permission {id CreatePermission.id,
|
|
103
|
+
resourceFqName CreatePermission.resourceFqName,
|
|
104
|
+
c CreatePermission.c,
|
|
105
|
+
r CreatePermission.r,
|
|
106
|
+
u CreatePermission.u,
|
|
107
|
+
d CreatePermission.d},
|
|
108
|
+
RolePermission {Role {name? CreatePermission.roleName}},
|
|
109
|
+
@upsert}
|
|
92
110
|
}
|
|
93
111
|
|
|
94
112
|
workflow AddPermissionToRole {
|
|
95
113
|
{Role {name? AddPermissionToRole.roleName}} as role;
|
|
96
114
|
{Permission {id? AddPermissionToRole.permissionId}} as perm;
|
|
97
|
-
|
|
115
|
+
{RolePermission {Role role, Permission perm}, @upsert}
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
workflow FindRolePermissions {
|
|
@@ -109,6 +127,7 @@ entity Session {
|
|
|
109
127
|
isActive Boolean
|
|
110
128
|
}
|
|
111
129
|
|
|
130
|
+
|
|
112
131
|
workflow CreateSession {
|
|
113
132
|
{Session {id CreateSession.id, userId CreateSession.userId,
|
|
114
133
|
authToken CreateSession.authToken, isActive true}}
|
|
@@ -128,6 +147,7 @@ workflow RemoveSession {
|
|
|
128
147
|
purge {Session {id? RemoveSession.id}}
|
|
129
148
|
}
|
|
130
149
|
|
|
150
|
+
|
|
131
151
|
workflow signup {
|
|
132
152
|
await Auth.signUpUser(signup.email, signup.password, signup.userData)
|
|
133
153
|
}
|
|
@@ -135,6 +155,14 @@ workflow signup {
|
|
|
135
155
|
workflow login {
|
|
136
156
|
await Auth.loginUser(login.email, login.password)
|
|
137
157
|
}
|
|
158
|
+
|
|
159
|
+
workflow getUser {
|
|
160
|
+
await Auth.getUserInfo(getUser.userId)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
workflow getUserByEmail {
|
|
164
|
+
await Auth.getUserInfoByEmail(getUserByEmail.email)
|
|
165
|
+
}
|
|
138
166
|
`;
|
|
139
167
|
|
|
140
168
|
const evalEvent = makeEventEvaluator(CoreAuthModuleName);
|
|
@@ -298,11 +326,25 @@ export async function assignUserToRole(
|
|
|
298
326
|
return r;
|
|
299
327
|
}
|
|
300
328
|
|
|
329
|
+
let DefaultRoleInstance: Instance | undefined;
|
|
330
|
+
|
|
301
331
|
export async function findUserRoles(userId: string, env: Environment): Promise<Result> {
|
|
302
332
|
const result: any = await evalEvent('FindUserRoles', { userId: userId }, env);
|
|
303
333
|
const inst: Instance | undefined = result ? (result[0] as Instance) : undefined;
|
|
304
334
|
if (inst) {
|
|
305
|
-
|
|
335
|
+
let roles: Instance[] | undefined = inst.getRelatedInstances('UserRole');
|
|
336
|
+
if (roles == undefined) {
|
|
337
|
+
roles = [];
|
|
338
|
+
}
|
|
339
|
+
if (DefaultRoleInstance == undefined) {
|
|
340
|
+
DefaultRoleInstance = makeInstance(
|
|
341
|
+
CoreAuthModuleName,
|
|
342
|
+
'Role',
|
|
343
|
+
newInstanceAttributes().set('name', '*')
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
roles.push(DefaultRoleInstance);
|
|
347
|
+
return roles;
|
|
306
348
|
}
|
|
307
349
|
return undefined;
|
|
308
350
|
}
|
|
@@ -344,7 +386,7 @@ export async function userHasPermissions(
|
|
|
344
386
|
perms: Set<RbacPermissionFlag>,
|
|
345
387
|
env: Environment
|
|
346
388
|
): Promise<boolean> {
|
|
347
|
-
if (userId == AdminUserId) {
|
|
389
|
+
if (userId == AdminUserId || !isRbacEnabled()) {
|
|
348
390
|
return true;
|
|
349
391
|
}
|
|
350
392
|
let userRoles: string[] | undefined = UserRoleCache.get(userId);
|
|
@@ -437,43 +479,197 @@ export async function signUpUser(
|
|
|
437
479
|
env: Environment
|
|
438
480
|
): Promise<UserInfo> {
|
|
439
481
|
let result: any;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
482
|
+
try {
|
|
483
|
+
await fetchAuthImpl().signUp(
|
|
484
|
+
username,
|
|
485
|
+
password,
|
|
486
|
+
userData ? new Map(Object.entries(userData)) : undefined,
|
|
487
|
+
env,
|
|
488
|
+
(userInfo: UserInfo) => {
|
|
489
|
+
result = userInfo;
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
return result as UserInfo;
|
|
493
|
+
} catch (err: any) {
|
|
494
|
+
logger.error(`Signup failed for ${username}: ${err.message}`);
|
|
495
|
+
throw err; // Re-throw to preserve error type for HTTP status mapping
|
|
496
|
+
}
|
|
450
497
|
}
|
|
451
498
|
|
|
452
499
|
export async function loginUser(
|
|
453
500
|
username: string,
|
|
454
501
|
password: string,
|
|
455
502
|
env: Environment
|
|
456
|
-
): Promise<string> {
|
|
457
|
-
let result: string = '';
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
503
|
+
): Promise<string | object> {
|
|
504
|
+
let result: string | object = '';
|
|
505
|
+
try {
|
|
506
|
+
await fetchAuthImpl().login(username, password, env, (r: SessionInfo) => {
|
|
507
|
+
// Check if Cognito is configured by checking if we have the tokens
|
|
508
|
+
if (r.idToken && r.accessToken && r.refreshToken) {
|
|
509
|
+
// Return full token response for Cognito
|
|
510
|
+
result = {
|
|
511
|
+
id_token: r.idToken,
|
|
512
|
+
access_token: r.accessToken,
|
|
513
|
+
refresh_token: r.refreshToken,
|
|
514
|
+
token_type: 'Bearer',
|
|
515
|
+
expires_in: 3600,
|
|
516
|
+
userId: r.userId,
|
|
517
|
+
sessionId: r.sessionId,
|
|
518
|
+
};
|
|
519
|
+
} else {
|
|
520
|
+
// Return string format for non-Cognito authentication
|
|
521
|
+
result = `${r.userId}/${r.sessionId}`;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
return result;
|
|
525
|
+
} catch (err: any) {
|
|
526
|
+
logger.error(`Login failed for ${username}: ${err.message}`);
|
|
527
|
+
throw err; // Re-throw to preserve error type for HTTP status mapping
|
|
528
|
+
}
|
|
462
529
|
}
|
|
463
530
|
|
|
464
531
|
export async function verifySession(token: string, env?: Environment): Promise<ActiveSessionInfo> {
|
|
465
532
|
if (!isAuthEnabled()) return BypassSession;
|
|
533
|
+
|
|
534
|
+
// Check if token is a JWT (Cognito ID token) or userId/sessionId format
|
|
535
|
+
if (isJwtToken(token)) {
|
|
536
|
+
return await verifyJwtToken(token, env);
|
|
537
|
+
} else {
|
|
538
|
+
return await verifySessionToken(token, env);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function isJwtToken(token: string): boolean {
|
|
543
|
+
// Simple JWT structure check - JWT tokens have 3 parts separated by dots
|
|
544
|
+
return !!(token && typeof token === 'string' && token.split('.').length === 3);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveSessionInfo> {
|
|
548
|
+
const needCommit = env ? false : true;
|
|
549
|
+
env = env ? env : new Environment();
|
|
550
|
+
const f = async () => {
|
|
551
|
+
try {
|
|
552
|
+
// Validate JWT structure first
|
|
553
|
+
if (!isJwtToken(token)) {
|
|
554
|
+
throw new UnauthorisedError('Invalid JWT token structure');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Verify the JWT token directly with Cognito
|
|
558
|
+
await fetchAuthImpl().verifyToken(token, env);
|
|
559
|
+
|
|
560
|
+
// Extract user information from JWT payload
|
|
561
|
+
const parts = token.split('.');
|
|
562
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
563
|
+
|
|
564
|
+
// Extract user ID from standard JWT claims (sub or cognito:username)
|
|
565
|
+
const userId = payload.sub || payload['cognito:username'];
|
|
566
|
+
const email = payload.email || payload['cognito:username'];
|
|
567
|
+
|
|
568
|
+
if (!userId) {
|
|
569
|
+
throw new UnauthorisedError('Invalid JWT token: missing user identifier');
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
let localUser = null;
|
|
573
|
+
if (email) {
|
|
574
|
+
localUser = await findUserByEmail(email, env);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (!localUser && userId) {
|
|
578
|
+
localUser = await findUser(userId, env);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (!localUser) {
|
|
582
|
+
logger.warn(
|
|
583
|
+
`User not found in local database for JWT token. Email: ${email}, UserId: ${userId}`
|
|
584
|
+
);
|
|
585
|
+
throw new UnauthorisedError(`User not found in local database`);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Use the local user's ID for consistency
|
|
589
|
+
const localUserId = localUser.lookup('id');
|
|
590
|
+
|
|
591
|
+
// For JWT tokens, we use the token itself as sessionId for tracking
|
|
592
|
+
return { sessionId: token.substring(0, 32), userId: localUserId };
|
|
593
|
+
} catch (err: any) {
|
|
594
|
+
if (err instanceof UnauthorisedError) {
|
|
595
|
+
throw err;
|
|
596
|
+
}
|
|
597
|
+
logger.error(`JWT token verification failed:`, {
|
|
598
|
+
errorName: err.name,
|
|
599
|
+
errorMessage: err.message,
|
|
600
|
+
});
|
|
601
|
+
throw new UnauthorisedError('JWT token verification failed');
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
if (needCommit) {
|
|
605
|
+
return await env.callInTransaction(f);
|
|
606
|
+
} else {
|
|
607
|
+
return await f();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async function verifySessionToken(token: string, env?: Environment): Promise<ActiveSessionInfo> {
|
|
466
612
|
const parts = token.split('/');
|
|
467
613
|
const sessId = parts[1];
|
|
468
614
|
const needCommit = env ? false : true;
|
|
469
615
|
env = env ? env : new Environment();
|
|
470
616
|
const f = async () => {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
617
|
+
try {
|
|
618
|
+
const sess: Instance = await findSession(sessId, env);
|
|
619
|
+
if (sess != undefined) {
|
|
620
|
+
await fetchAuthImpl().verifyToken(sess.lookup('authToken'), env);
|
|
621
|
+
return { sessionId: sessId, userId: parts[0] };
|
|
622
|
+
} else {
|
|
623
|
+
logger.warn(`No active session found for user '${parts[0]}'`);
|
|
624
|
+
throw new UnauthorisedError(`No active session for user '${parts[0]}'`);
|
|
625
|
+
}
|
|
626
|
+
} catch (err: any) {
|
|
627
|
+
if (err instanceof UnauthorisedError) {
|
|
628
|
+
throw err;
|
|
629
|
+
}
|
|
630
|
+
// Log error details for debugging
|
|
631
|
+
logger.error(`Session verification failed for user '${parts[0]}':`, {
|
|
632
|
+
errorName: err.name,
|
|
633
|
+
errorMessage: err.message,
|
|
634
|
+
sessionId: sessId,
|
|
635
|
+
});
|
|
636
|
+
throw new UnauthorisedError('Session verification failed');
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
if (needCommit) {
|
|
640
|
+
return await env.callInTransaction(f);
|
|
641
|
+
} else {
|
|
642
|
+
return await f();
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export async function getUserInfo(userId: string, env: Environment): Promise<UserInfo> {
|
|
647
|
+
const needCommit = env ? false : true;
|
|
648
|
+
env = env ? env : new Environment();
|
|
649
|
+
const f = async () => {
|
|
650
|
+
try {
|
|
651
|
+
return await fetchAuthImpl().getUser(userId, env);
|
|
652
|
+
} catch (err: any) {
|
|
653
|
+
logger.error(`Failed to get user info for ${userId}: ${err.message}`);
|
|
654
|
+
throw err; // Re-throw to preserve error type
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
if (needCommit) {
|
|
658
|
+
return await env.callInTransaction(f);
|
|
659
|
+
} else {
|
|
660
|
+
return await f();
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export async function getUserInfoByEmail(email: string, env: Environment): Promise<UserInfo> {
|
|
665
|
+
const needCommit = env ? false : true;
|
|
666
|
+
env = env ? env : new Environment();
|
|
667
|
+
const f = async () => {
|
|
668
|
+
try {
|
|
669
|
+
return await fetchAuthImpl().getUserByEmail(email, env);
|
|
670
|
+
} catch (err: any) {
|
|
671
|
+
logger.error(`Failed to get user info for email ${email}: ${err.message}`);
|
|
672
|
+
throw err; // Re-throw to preserve error type
|
|
477
673
|
}
|
|
478
674
|
};
|
|
479
675
|
if (needCommit) {
|
|
@@ -487,3 +683,99 @@ export function requireAuth(moduleName: string, eventName: string): boolean {
|
|
|
487
683
|
const f = moduleName == CoreAuthModuleName && (eventName == 'login' || eventName == 'signup');
|
|
488
684
|
return !f;
|
|
489
685
|
}
|
|
686
|
+
|
|
687
|
+
// Export getHttpStatusForError for use in HTTP handlers
|
|
688
|
+
export { getHttpStatusForError };
|
|
689
|
+
|
|
690
|
+
// Helper function to create standardized error responses
|
|
691
|
+
export function createAuthErrorResponse(error: Error): {
|
|
692
|
+
error: string;
|
|
693
|
+
message: string;
|
|
694
|
+
statusCode: number;
|
|
695
|
+
} {
|
|
696
|
+
const statusCode = getHttpStatusForError(error);
|
|
697
|
+
let errorType = 'AUTHENTICATION_ERROR';
|
|
698
|
+
|
|
699
|
+
if (error instanceof UserNotFoundError) {
|
|
700
|
+
errorType = 'USER_NOT_FOUND';
|
|
701
|
+
} else if (error instanceof UnauthorisedError) {
|
|
702
|
+
errorType = 'UNAUTHORIZED';
|
|
703
|
+
} else if (error instanceof UserNotConfirmedError) {
|
|
704
|
+
errorType = 'USER_NOT_CONFIRMED';
|
|
705
|
+
} else if (error instanceof PasswordResetRequiredError) {
|
|
706
|
+
errorType = 'PASSWORD_RESET_REQUIRED';
|
|
707
|
+
} else if (error instanceof TooManyRequestsError) {
|
|
708
|
+
errorType = 'TOO_MANY_REQUESTS';
|
|
709
|
+
} else if (error instanceof InvalidParameterError) {
|
|
710
|
+
errorType = 'INVALID_PARAMETER';
|
|
711
|
+
} else if (error instanceof ExpiredCodeError) {
|
|
712
|
+
errorType = 'EXPIRED_CODE';
|
|
713
|
+
} else if (error instanceof CodeMismatchError) {
|
|
714
|
+
errorType = 'CODE_MISMATCH';
|
|
715
|
+
} else if (error instanceof BadRequestError) {
|
|
716
|
+
errorType = 'BAD_REQUEST';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Log error creation for debugging purposes
|
|
720
|
+
logger.debug(`Creating auth error response:`, {
|
|
721
|
+
errorType: errorType,
|
|
722
|
+
statusCode: statusCode,
|
|
723
|
+
originalError: error.name,
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
return {
|
|
727
|
+
error: errorType,
|
|
728
|
+
message: error.message,
|
|
729
|
+
statusCode: statusCode,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Helper function to check if an error is a known auth error
|
|
734
|
+
export function isAuthError(error: any): boolean {
|
|
735
|
+
return (
|
|
736
|
+
error instanceof UnauthorisedError ||
|
|
737
|
+
error instanceof UserNotFoundError ||
|
|
738
|
+
error instanceof UserNotConfirmedError ||
|
|
739
|
+
error instanceof PasswordResetRequiredError ||
|
|
740
|
+
error instanceof TooManyRequestsError ||
|
|
741
|
+
error instanceof InvalidParameterError ||
|
|
742
|
+
error instanceof ExpiredCodeError ||
|
|
743
|
+
error instanceof CodeMismatchError ||
|
|
744
|
+
error instanceof BadRequestError
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Helper function to sanitize error details before logging
|
|
749
|
+
export function sanitizeErrorForLogging(error: Error): {
|
|
750
|
+
name: string;
|
|
751
|
+
message: string;
|
|
752
|
+
sanitizedMessage: string;
|
|
753
|
+
} {
|
|
754
|
+
const sanitizedMessage = error.message
|
|
755
|
+
.replace(/password/gi, '[REDACTED]')
|
|
756
|
+
.replace(/token/gi, '[REDACTED]')
|
|
757
|
+
.replace(/secret/gi, '[REDACTED]')
|
|
758
|
+
.replace(/key/gi, '[REDACTED]')
|
|
759
|
+
.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[EMAIL_REDACTED]')
|
|
760
|
+
.replace(/\b[A-Fa-f0-9]{32,}\b/g, '[TOKEN_REDACTED]')
|
|
761
|
+
.replace(/\b\d{4,}\b/g, '[NUMBER_REDACTED]');
|
|
762
|
+
|
|
763
|
+
return {
|
|
764
|
+
name: error.name,
|
|
765
|
+
message: error.message,
|
|
766
|
+
sanitizedMessage: sanitizedMessage,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Helper function to determine if an error should be retried
|
|
771
|
+
export function isRetryableError(error: Error): boolean {
|
|
772
|
+
// Only retry on certain types of errors
|
|
773
|
+
return (
|
|
774
|
+
error instanceof TooManyRequestsError ||
|
|
775
|
+
(error.message
|
|
776
|
+
? error.message.includes('temporarily unavailable') ||
|
|
777
|
+
error.message.includes('service error') ||
|
|
778
|
+
error.message.includes('timeout')
|
|
779
|
+
: false)
|
|
780
|
+
);
|
|
781
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { default as ai } from './ai.js';
|
|
2
2
|
import { default as auth } from './auth.js';
|
|
3
|
-
import { DefaultModuleName } from '../util.js';
|
|
3
|
+
import { DefaultModuleName, DefaultModules } from '../util.js';
|
|
4
4
|
import { Instance, isInstanceOfType } from '../module.js';
|
|
5
5
|
import { Environment, parseAndEvaluateStatement } from '../interpreter.js';
|
|
6
6
|
import { logger } from '../logger.js';
|
|
@@ -27,9 +27,11 @@ entity auditlog {
|
|
|
27
27
|
export const CoreModules: string[] = [];
|
|
28
28
|
|
|
29
29
|
export function registerCoreModules() {
|
|
30
|
+
DefaultModules.add(DefaultModuleName);
|
|
30
31
|
CoreModules.push(CoreModuleDefinition);
|
|
31
32
|
[auth, ai].forEach((mdef: string) => {
|
|
32
33
|
CoreModules.push(mdef);
|
|
34
|
+
DefaultModules.add(mdef);
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
37
|
|
package/src/runtime/relgraph.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fetchModule,
|
|
3
|
-
getUserModuleNames,
|
|
4
|
-
Relationship,
|
|
5
|
-
RelationshipNode,
|
|
6
|
-
Module,
|
|
7
|
-
} from './module.js';
|
|
1
|
+
import { fetchModule, Relationship, RelationshipNode, Module, getModuleNames } from './module.js';
|
|
8
2
|
import { DefaultModuleName, Path } from './util.js';
|
|
9
3
|
|
|
10
4
|
export type RelationshipGraphNode = {
|
|
@@ -86,7 +80,7 @@ export function buildGraph(moduleName: string): RelationshipGraph {
|
|
|
86
80
|
const inRels: Set<string> = new Set();
|
|
87
81
|
const nodes: Array<RelationshipGraphNode> = [];
|
|
88
82
|
let localMod: Module | undefined;
|
|
89
|
-
|
|
83
|
+
getModuleNames().forEach((n: string) => {
|
|
90
84
|
const m: Module = fetchModule(n);
|
|
91
85
|
if (n == moduleName) localMod = m;
|
|
92
86
|
const rels: Relationship[] = m.getRelationshipEntries();
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { evaluate } from '../interpreter.js';
|
|
2
|
+
import { logger } from '../logger.js';
|
|
3
|
+
import {
|
|
4
|
+
Instance,
|
|
5
|
+
InstanceAttributes,
|
|
6
|
+
makeInstance,
|
|
7
|
+
newInstanceAttributes,
|
|
8
|
+
Relationship,
|
|
9
|
+
} from '../module.js';
|
|
10
|
+
import { splitFqName } from '../util.js';
|
|
2
11
|
|
|
3
12
|
export class ResolverAuthInfo {
|
|
4
13
|
userId: string;
|
|
@@ -24,6 +33,16 @@ export type JoinInfo = {
|
|
|
24
33
|
subJoins: JoinInfo[] | undefined;
|
|
25
34
|
};
|
|
26
35
|
|
|
36
|
+
const subscriptionEvents: Map<string, string> = new Map<string, string>();
|
|
37
|
+
|
|
38
|
+
export function setSubscriptionEvent(fqEventName: string, resolverName: string) {
|
|
39
|
+
subscriptionEvents.set(resolverName, fqEventName);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getSubscriptionEvent(resolverName: string): string | undefined {
|
|
43
|
+
return subscriptionEvents.get(resolverName);
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
export class Resolver {
|
|
28
47
|
protected authInfo: ResolverAuthInfo = DefaultAuthInfo;
|
|
29
48
|
protected userData: any;
|
|
@@ -31,6 +50,10 @@ export class Resolver {
|
|
|
31
50
|
|
|
32
51
|
static Default = new Resolver();
|
|
33
52
|
|
|
53
|
+
constructor(name?: string) {
|
|
54
|
+
if (name) this.name = name;
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
public setAuthInfo(authInfo: ResolverAuthInfo): Resolver {
|
|
35
58
|
this.authInfo = authInfo;
|
|
36
59
|
return this;
|
|
@@ -49,8 +72,8 @@ export class Resolver {
|
|
|
49
72
|
return this.name;
|
|
50
73
|
}
|
|
51
74
|
|
|
52
|
-
|
|
53
|
-
|
|
75
|
+
protected notImpl(method: string) {
|
|
76
|
+
logger.warn(`Method ${method} not implemented in resolver ${this.name}`);
|
|
54
77
|
}
|
|
55
78
|
|
|
56
79
|
public onSetPath(moduleName: string, entryName: string): any {
|
|
@@ -147,14 +170,126 @@ export class Resolver {
|
|
|
147
170
|
|
|
148
171
|
// Return a transactionId
|
|
149
172
|
public async startTransaction(): Promise<any> {
|
|
150
|
-
|
|
173
|
+
this.notImpl('startTransaction()');
|
|
174
|
+
return 1;
|
|
151
175
|
}
|
|
152
176
|
|
|
153
177
|
public async commitTransaction(txnId: string): Promise<any> {
|
|
154
178
|
return this.notImpl(`commitTransaction(${txnId})`);
|
|
155
179
|
}
|
|
156
180
|
|
|
157
|
-
public async rollbackTransaction(
|
|
158
|
-
return this.notImpl(`rollbackTransaction(${
|
|
181
|
+
public async rollbackTransaction(txnId: string): Promise<any> {
|
|
182
|
+
return this.notImpl(`rollbackTransaction(${txnId})`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async subscribe(): Promise<any> {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public async onSubscription(result: any): Promise<any> {
|
|
190
|
+
if (result != undefined) {
|
|
191
|
+
const eventName = getSubscriptionEvent(this.name);
|
|
192
|
+
if (eventName) {
|
|
193
|
+
const path = splitFqName(eventName);
|
|
194
|
+
const inst = makeInstance(
|
|
195
|
+
path.getModuleName(),
|
|
196
|
+
path.getEntryName(),
|
|
197
|
+
newInstanceAttributes().set('data', result)
|
|
198
|
+
);
|
|
199
|
+
return await evaluate(inst);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type MaybeFunction = Function | undefined;
|
|
206
|
+
|
|
207
|
+
export type GenericResolverMethods = {
|
|
208
|
+
create: MaybeFunction;
|
|
209
|
+
upsert: MaybeFunction;
|
|
210
|
+
update: MaybeFunction;
|
|
211
|
+
query: MaybeFunction;
|
|
212
|
+
delete: MaybeFunction;
|
|
213
|
+
startTransaction: MaybeFunction;
|
|
214
|
+
commitTransaction: MaybeFunction;
|
|
215
|
+
rollbackTransaction: MaybeFunction;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export type GenericResolverSubscription = {
|
|
219
|
+
subscribe: MaybeFunction;
|
|
220
|
+
onSubscriptionEvent: string;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export class GenericResolver extends Resolver {
|
|
224
|
+
implementation: GenericResolverMethods | undefined;
|
|
225
|
+
subs: GenericResolverSubscription | undefined;
|
|
226
|
+
|
|
227
|
+
constructor(name: string, implementation?: GenericResolverMethods) {
|
|
228
|
+
super(name);
|
|
229
|
+
this.implementation = implementation;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public override async createInstance(inst: Instance): Promise<any> {
|
|
233
|
+
if (this.implementation?.create) {
|
|
234
|
+
return await this.implementation.create(this, inst);
|
|
235
|
+
} else {
|
|
236
|
+
return await super.createInstance(inst);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public override async upsertInstance(inst: Instance): Promise<any> {
|
|
241
|
+
if (this.implementation?.upsert) {
|
|
242
|
+
return await this.implementation.upsert(this, inst);
|
|
243
|
+
}
|
|
244
|
+
return await super.upsertInstance(inst);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public override async updateInstance(inst: Instance, newAttrs: InstanceAttributes): Promise<any> {
|
|
248
|
+
if (this.implementation?.update) {
|
|
249
|
+
return await this.implementation.update(this, inst, newAttrs);
|
|
250
|
+
}
|
|
251
|
+
return await super.updateInstance(inst, newAttrs);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
public override async queryInstances(inst: Instance, queryAll: boolean): Promise<any> {
|
|
255
|
+
if (this.implementation?.query) {
|
|
256
|
+
return await this.implementation.query(this, inst, queryAll);
|
|
257
|
+
}
|
|
258
|
+
return await super.queryInstances(inst, queryAll);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public override async deleteInstance(inst: Instance | Instance[], purge: boolean): Promise<any> {
|
|
262
|
+
if (this.implementation?.delete) {
|
|
263
|
+
return await this.implementation.delete(this, inst, purge);
|
|
264
|
+
}
|
|
265
|
+
return await super.deleteInstance(inst, purge);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public override async startTransaction(): Promise<any> {
|
|
269
|
+
if (this.implementation?.startTransaction) {
|
|
270
|
+
return await this.implementation.startTransaction(this);
|
|
271
|
+
}
|
|
272
|
+
return await super.startTransaction();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public override async commitTransaction(txnId: string): Promise<any> {
|
|
276
|
+
if (this.implementation?.commitTransaction) {
|
|
277
|
+
return await this.implementation.commitTransaction(this, txnId);
|
|
278
|
+
}
|
|
279
|
+
return await super.commitTransaction(txnId);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
public override async rollbackTransaction(txnId: string): Promise<any> {
|
|
283
|
+
if (this.implementation?.rollbackTransaction) {
|
|
284
|
+
return await this.implementation.rollbackTransaction(this, txnId);
|
|
285
|
+
}
|
|
286
|
+
return await super.rollbackTransaction(txnId);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
override async subscribe() {
|
|
290
|
+
if (this.subs?.subscribe) {
|
|
291
|
+
await this.subs.subscribe(this);
|
|
292
|
+
}
|
|
293
|
+
await super.subscribe();
|
|
159
294
|
}
|
|
160
295
|
}
|