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.
Files changed (113) hide show
  1. package/README.md +16 -47
  2. package/out/api/http.d.ts.map +1 -1
  3. package/out/api/http.js +12 -0
  4. package/out/api/http.js.map +1 -1
  5. package/out/cli/main.d.ts +1 -3
  6. package/out/cli/main.d.ts.map +1 -1
  7. package/out/cli/main.js +6 -12
  8. package/out/cli/main.js.map +1 -1
  9. package/out/language/generated/ast.d.ts +80 -18
  10. package/out/language/generated/ast.d.ts.map +1 -1
  11. package/out/language/generated/ast.js +119 -26
  12. package/out/language/generated/ast.js.map +1 -1
  13. package/out/language/generated/grammar.d.ts +1 -1
  14. package/out/language/generated/grammar.d.ts.map +1 -1
  15. package/out/language/generated/grammar.js +831 -238
  16. package/out/language/generated/grammar.js.map +1 -1
  17. package/out/language/generated/module.d.ts +1 -1
  18. package/out/language/generated/module.js +1 -1
  19. package/out/language/main.cjs +995 -331
  20. package/out/language/main.cjs.map +2 -2
  21. package/out/language/parser.js +13 -6
  22. package/out/language/parser.js.map +1 -1
  23. package/out/runtime/agents/common.d.ts +1 -1
  24. package/out/runtime/agents/common.d.ts.map +1 -1
  25. package/out/runtime/agents/common.js +1 -1
  26. package/out/runtime/auth/cognito.d.ts +4 -1
  27. package/out/runtime/auth/cognito.d.ts.map +1 -1
  28. package/out/runtime/auth/cognito.js +540 -73
  29. package/out/runtime/auth/cognito.js.map +1 -1
  30. package/out/runtime/auth/defs.d.ts +3 -0
  31. package/out/runtime/auth/defs.d.ts.map +1 -1
  32. package/out/runtime/auth/defs.js +17 -1
  33. package/out/runtime/auth/defs.js.map +1 -1
  34. package/out/runtime/auth/interface.d.ts +6 -1
  35. package/out/runtime/auth/interface.d.ts.map +1 -1
  36. package/out/runtime/defs.d.ts +21 -0
  37. package/out/runtime/defs.d.ts.map +1 -1
  38. package/out/runtime/defs.js +35 -0
  39. package/out/runtime/defs.js.map +1 -1
  40. package/out/runtime/interpreter.d.ts.map +1 -1
  41. package/out/runtime/interpreter.js +45 -36
  42. package/out/runtime/interpreter.js.map +1 -1
  43. package/out/runtime/loader.d.ts +4 -2
  44. package/out/runtime/loader.d.ts.map +1 -1
  45. package/out/runtime/loader.js +148 -29
  46. package/out/runtime/loader.js.map +1 -1
  47. package/out/runtime/module.d.ts +48 -5
  48. package/out/runtime/module.d.ts.map +1 -1
  49. package/out/runtime/module.js +200 -9
  50. package/out/runtime/module.js.map +1 -1
  51. package/out/runtime/modules/ai.d.ts +7 -5
  52. package/out/runtime/modules/ai.d.ts.map +1 -1
  53. package/out/runtime/modules/ai.js +50 -24
  54. package/out/runtime/modules/ai.js.map +1 -1
  55. package/out/runtime/modules/auth.d.ts +17 -1
  56. package/out/runtime/modules/auth.d.ts.map +1 -1
  57. package/out/runtime/modules/auth.js +282 -30
  58. package/out/runtime/modules/auth.js.map +1 -1
  59. package/out/runtime/modules/core.d.ts.map +1 -1
  60. package/out/runtime/modules/core.js +3 -1
  61. package/out/runtime/modules/core.js.map +1 -1
  62. package/out/runtime/relgraph.d.ts.map +1 -1
  63. package/out/runtime/relgraph.js +2 -2
  64. package/out/runtime/relgraph.js.map +1 -1
  65. package/out/runtime/resolvers/interface.d.ts +37 -2
  66. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  67. package/out/runtime/resolvers/interface.js +103 -5
  68. package/out/runtime/resolvers/interface.js.map +1 -1
  69. package/out/runtime/resolvers/registry.d.ts +3 -2
  70. package/out/runtime/resolvers/registry.d.ts.map +1 -1
  71. package/out/runtime/resolvers/registry.js +3 -0
  72. package/out/runtime/resolvers/registry.js.map +1 -1
  73. package/out/runtime/state.d.ts +31 -3
  74. package/out/runtime/state.d.ts.map +1 -1
  75. package/out/runtime/state.js +11 -1
  76. package/out/runtime/state.js.map +1 -1
  77. package/out/runtime/util.d.ts +4 -0
  78. package/out/runtime/util.d.ts.map +1 -1
  79. package/out/runtime/util.js +16 -0
  80. package/out/runtime/util.js.map +1 -1
  81. package/out/syntaxes/agentlang.monarch.js +2 -2
  82. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  83. package/out/utils/http.d.ts +2 -0
  84. package/out/utils/http.d.ts.map +1 -0
  85. package/out/utils/http.js +5 -0
  86. package/out/utils/http.js.map +1 -0
  87. package/package.json +8 -6
  88. package/src/api/http.ts +15 -0
  89. package/src/cli/main.ts +6 -12
  90. package/src/language/agentlang.langium +31 -10
  91. package/src/language/generated/ast.ts +212 -44
  92. package/src/language/generated/grammar.ts +831 -238
  93. package/src/language/generated/module.ts +1 -1
  94. package/src/language/parser.ts +12 -8
  95. package/src/runtime/agents/common.ts +1 -1
  96. package/src/runtime/auth/cognito.ts +605 -74
  97. package/src/runtime/auth/defs.ts +17 -1
  98. package/src/runtime/auth/interface.ts +6 -1
  99. package/src/runtime/defs.ts +45 -0
  100. package/src/runtime/interpreter.ts +43 -34
  101. package/src/runtime/loader.ts +159 -30
  102. package/src/runtime/module.ts +243 -10
  103. package/src/runtime/modules/ai.ts +52 -28
  104. package/src/runtime/modules/auth.ts +330 -38
  105. package/src/runtime/modules/core.ts +3 -1
  106. package/src/runtime/relgraph.ts +2 -8
  107. package/src/runtime/resolvers/interface.ts +141 -6
  108. package/src/runtime/resolvers/registry.ts +5 -2
  109. package/src/runtime/state.ts +11 -1
  110. package/src/runtime/util.ts +17 -0
  111. package/src/syntaxes/agentlang.monarch.ts +2 -2
  112. package/src/utils/http.ts +5 -0
  113. 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 { ActiveSessionInfo, AdminUserId, BypassSession, isAuthEnabled } from '../auth/defs.js';
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
- upsert {Role {name CreateRole.name}}
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
- upsert {UserRole {User user, Role role}}
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
- upsert {UserRole {User user, Role role}}
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
- upsert {Permission {id CreatePermission.id,
86
- resourceFqName CreatePermission.resourceFqName,
87
- c CreatePermission.c,
88
- r CreatePermission.r,
89
- u CreatePermission.u,
90
- d CreatePermission.d},
91
- RolePermission {Role {name? CreatePermission.roleName}}}
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
- upsert {RolePermission {Role role, Permission perm}}
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
- return inst.getRelatedInstances('UserRole');
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
- await fetchAuthImpl().signUp(
441
- username,
442
- password,
443
- userData ? new Map(Object.entries(userData)) : undefined,
444
- env,
445
- (userInfo: UserInfo) => {
446
- result = userInfo;
447
- }
448
- );
449
- return result as UserInfo;
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
- await fetchAuthImpl().login(username, password, env, (r: SessionInfo) => {
459
- result = `${r.userId}/${r.sessionId}`;
460
- });
461
- return result;
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
- const sess: Instance = await findSession(sessId, env);
472
- if (sess != undefined) {
473
- await fetchAuthImpl().verifyToken(sess.lookup('authToken'), env);
474
- return { sessionId: sessId, userId: parts[0] };
475
- } else {
476
- throw new Error(`No active session for user '${parts[0]}'`);
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
 
@@ -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
- getUserModuleNames().forEach((n: string) => {
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 { Instance, InstanceAttributes, Relationship } from '../module.js';
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
- private notImpl(method: string) {
53
- throw new Error(`Resolver method ${method} not implemented`);
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
- return this.notImpl('startTransaction()');
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(txtIn: string): Promise<any> {
158
- return this.notImpl(`rollbackTransaction(${txtIn})`);
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
  }