agentlang 0.10.9 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +60 -0
  2. package/out/api/http.d.ts.map +1 -1
  3. package/out/api/http.js +64 -39
  4. package/out/api/http.js.map +1 -1
  5. package/out/cli/main.d.ts +1 -1
  6. package/out/cli/main.d.ts.map +1 -1
  7. package/out/cli/main.js +7 -3
  8. package/out/cli/main.js.map +1 -1
  9. package/out/extension/main.cjs +250 -250
  10. package/out/extension/main.cjs.map +2 -2
  11. package/out/language/main.cjs +504 -504
  12. package/out/language/main.cjs.map +3 -3
  13. package/out/runtime/auth/cognito.d.ts.map +1 -1
  14. package/out/runtime/auth/cognito.js +129 -64
  15. package/out/runtime/auth/cognito.js.map +1 -1
  16. package/out/runtime/defs.d.ts +22 -9
  17. package/out/runtime/defs.d.ts.map +1 -1
  18. package/out/runtime/defs.js +44 -9
  19. package/out/runtime/defs.js.map +1 -1
  20. package/out/runtime/document-retriever.d.ts +24 -0
  21. package/out/runtime/document-retriever.d.ts.map +1 -0
  22. package/out/runtime/document-retriever.js +258 -0
  23. package/out/runtime/document-retriever.js.map +1 -0
  24. package/out/runtime/errors/coded-error.d.ts +8 -0
  25. package/out/runtime/errors/coded-error.d.ts.map +1 -0
  26. package/out/runtime/errors/coded-error.js +13 -0
  27. package/out/runtime/errors/coded-error.js.map +1 -0
  28. package/out/runtime/errors/http-error.d.ts +25 -0
  29. package/out/runtime/errors/http-error.d.ts.map +1 -0
  30. package/out/runtime/errors/http-error.js +169 -0
  31. package/out/runtime/errors/http-error.js.map +1 -0
  32. package/out/runtime/excel-resolver.d.ts +4 -0
  33. package/out/runtime/excel-resolver.d.ts.map +1 -0
  34. package/out/runtime/excel-resolver.js +96 -0
  35. package/out/runtime/excel-resolver.js.map +1 -0
  36. package/out/runtime/excel.d.ts +25 -0
  37. package/out/runtime/excel.d.ts.map +1 -0
  38. package/out/runtime/excel.js +127 -0
  39. package/out/runtime/excel.js.map +1 -0
  40. package/out/runtime/interpreter.d.ts.map +1 -1
  41. package/out/runtime/interpreter.js +26 -25
  42. package/out/runtime/interpreter.js.map +1 -1
  43. package/out/runtime/logger.d.ts +6 -0
  44. package/out/runtime/logger.d.ts.map +1 -1
  45. package/out/runtime/logger.js +21 -0
  46. package/out/runtime/logger.js.map +1 -1
  47. package/out/runtime/module.d.ts.map +1 -1
  48. package/out/runtime/module.js +14 -13
  49. package/out/runtime/module.js.map +1 -1
  50. package/out/runtime/modules/ai.d.ts +1 -0
  51. package/out/runtime/modules/ai.d.ts.map +1 -1
  52. package/out/runtime/modules/ai.js +1 -0
  53. package/out/runtime/modules/ai.js.map +1 -1
  54. package/out/runtime/modules/auth.d.ts.map +1 -1
  55. package/out/runtime/modules/auth.js +35 -12
  56. package/out/runtime/modules/auth.js.map +1 -1
  57. package/out/runtime/modules/core.d.ts +6 -0
  58. package/out/runtime/modules/core.d.ts.map +1 -1
  59. package/out/runtime/modules/core.js +20 -0
  60. package/out/runtime/modules/core.js.map +1 -1
  61. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  62. package/out/runtime/resolvers/sqldb/database.js +76 -39
  63. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  64. package/out/runtime/resolvers/sqldb/db-errors.d.ts +6 -0
  65. package/out/runtime/resolvers/sqldb/db-errors.d.ts.map +1 -0
  66. package/out/runtime/resolvers/sqldb/db-errors.js +100 -0
  67. package/out/runtime/resolvers/sqldb/db-errors.js.map +1 -0
  68. package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
  69. package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
  70. package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
  71. package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
  72. package/out/runtime/resolvers/vector/types.d.ts +32 -0
  73. package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
  74. package/out/runtime/resolvers/vector/types.js +2 -0
  75. package/out/runtime/resolvers/vector/types.js.map +1 -0
  76. package/out/runtime/state.d.ts +3 -0
  77. package/out/runtime/state.d.ts.map +1 -1
  78. package/out/runtime/state.js +7 -0
  79. package/out/runtime/state.js.map +1 -1
  80. package/out/setupClassic.d.ts +98 -0
  81. package/out/setupClassic.d.ts.map +1 -0
  82. package/out/setupClassic.js +38 -0
  83. package/out/setupClassic.js.map +1 -0
  84. package/out/setupCommon.d.ts +2 -0
  85. package/out/setupCommon.d.ts.map +1 -0
  86. package/out/setupCommon.js +33 -0
  87. package/out/setupCommon.js.map +1 -0
  88. package/out/setupExtended.d.ts +40 -0
  89. package/out/setupExtended.d.ts.map +1 -0
  90. package/out/setupExtended.js +67 -0
  91. package/out/setupExtended.js.map +1 -0
  92. package/package.json +19 -18
  93. package/src/api/http.ts +71 -37
  94. package/src/cli/main.ts +12 -4
  95. package/src/runtime/auth/cognito.ts +187 -65
  96. package/src/runtime/defs.ts +51 -18
  97. package/src/runtime/errors/coded-error.ts +18 -0
  98. package/src/runtime/errors/http-error.ts +197 -0
  99. package/src/runtime/interpreter.ts +70 -27
  100. package/src/runtime/logger.ts +27 -0
  101. package/src/runtime/module.ts +45 -13
  102. package/src/runtime/modules/ai.ts +1 -0
  103. package/src/runtime/modules/auth.ts +35 -12
  104. package/src/runtime/modules/core.ts +26 -0
  105. package/src/runtime/resolvers/sqldb/database.ts +88 -37
  106. package/src/runtime/resolvers/sqldb/db-errors.ts +113 -0
  107. package/src/runtime/state.ts +7 -0
  108. package/src/xlsx.d.ts +17 -0
@@ -27,6 +27,7 @@ import {
27
27
  set_getUserTenantId,
28
28
  PathAttributeName,
29
29
  } from '../defs.js';
30
+ import { createCodedError } from '../errors/coded-error.js';
30
31
  import {
31
32
  DbContext,
32
33
  getManyByRawQuery,
@@ -1045,7 +1046,7 @@ function fetchAuthImpl(): AgentlangAuth {
1045
1046
  if (runtimeAuth) {
1046
1047
  return runtimeAuth;
1047
1048
  } else {
1048
- throw new Error('Auth not initialized');
1049
+ throw createCodedError('Auth not initialized', 'AL_AUTH_NOT_INITIALIZED');
1049
1050
  }
1050
1051
  }
1051
1052
 
@@ -1115,7 +1116,9 @@ export async function forgotPasswordUser(username: string, env: Environment): Pr
1115
1116
  const email = username.toLowerCase();
1116
1117
  const allowed = await fetchAuthImpl().userExistsInIdentityProvider(email, env);
1117
1118
  if (!allowed) {
1118
- throw new UserNotFoundError('Email not registered');
1119
+ throw new UserNotFoundError('Email not registered', {
1120
+ agentlangCode: 'AL_AUTH_EMAIL_NOT_REGISTERED',
1121
+ });
1119
1122
  }
1120
1123
  await fetchAuthImpl().forgotPassword(email, env);
1121
1124
  return { status: 'ok', message: 'Password reset code sent' };
@@ -1265,7 +1268,9 @@ export async function changePassword(
1265
1268
  return undefined;
1266
1269
  }
1267
1270
  } else {
1268
- throw new UnauthorisedError(`No active session for user ${user}`);
1271
+ throw new UnauthorisedError(`No active session for user ${user}`, {
1272
+ agentlangCode: 'AL_AUTH_NO_ACTIVE_SESSION_CHANGE_PW',
1273
+ });
1269
1274
  }
1270
1275
  }
1271
1276
 
@@ -1292,7 +1297,9 @@ async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveS
1292
1297
  try {
1293
1298
  // Validate JWT structure first
1294
1299
  if (!isJwtToken(token)) {
1295
- throw new UnauthorisedError('Invalid JWT token structure');
1300
+ throw new UnauthorisedError('Invalid JWT token structure', {
1301
+ agentlangCode: 'AL_AUTH_JWT_STRUCTURE_INVALID',
1302
+ });
1296
1303
  }
1297
1304
 
1298
1305
  // Verify the JWT token directly with Cognito
@@ -1307,7 +1314,9 @@ async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveS
1307
1314
  const email = payload.email || payload['cognito:username'];
1308
1315
 
1309
1316
  if (!userId) {
1310
- throw new UnauthorisedError('Invalid JWT token: missing user identifier');
1317
+ throw new UnauthorisedError('Invalid JWT token: missing user identifier', {
1318
+ agentlangCode: 'AL_AUTH_JWT_MISSING_USER_ID',
1319
+ });
1311
1320
  }
1312
1321
 
1313
1322
  let localUser = null;
@@ -1323,7 +1332,9 @@ async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveS
1323
1332
  logger.warn(
1324
1333
  `User not found in local database for JWT token. Email: ${email}, UserId: ${userId}`
1325
1334
  );
1326
- throw new UnauthorisedError(`User not found in local database`);
1335
+ throw new UnauthorisedError(`User not found in local database`, {
1336
+ agentlangCode: 'AL_AUTH_JWT_USER_NOT_IN_DB',
1337
+ });
1327
1338
  }
1328
1339
 
1329
1340
  // Use the local user's ID for consistency
@@ -1332,12 +1343,16 @@ async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveS
1332
1343
  // Check if user status is 'Active'
1333
1344
  const userStatus = localUser.lookup('status');
1334
1345
  if (userStatus !== 'Active') {
1335
- throw new UnauthorisedError(`User account is not active. Status: ${userStatus}`);
1346
+ throw new UnauthorisedError(`User account is not active. Status: ${userStatus}`, {
1347
+ agentlangCode: 'AL_AUTH_USER_NOT_ACTIVE_JWT',
1348
+ });
1336
1349
  }
1337
1350
 
1338
1351
  const sess = await findUserSession(localUserId, env);
1339
1352
  if (!sess) {
1340
- throw new UnauthorisedError(`No session found for user ${email}, UserId: ${userId}`);
1353
+ throw new UnauthorisedError(`No session found for user ${email}, UserId: ${userId}`, {
1354
+ agentlangCode: 'AL_AUTH_NO_SESSION_JWT',
1355
+ });
1341
1356
  }
1342
1357
  // For JWT tokens, we use the token itself as sessionId for tracking
1343
1358
  return { sessionId: sess.lookup('id'), userId: localUserId };
@@ -1349,7 +1364,9 @@ async function verifyJwtToken(token: string, env?: Environment): Promise<ActiveS
1349
1364
  errorName: err.name,
1350
1365
  errorMessage: err.message,
1351
1366
  });
1352
- throw new UnauthorisedError('JWT token verification failed');
1367
+ throw new UnauthorisedError('JWT token verification failed', {
1368
+ agentlangCode: 'AL_AUTH_JWT_VERIFY_WRAPPER',
1369
+ });
1353
1370
  }
1354
1371
  };
1355
1372
  if (needCommit) {
@@ -1372,7 +1389,9 @@ async function verifySessionToken(token: string, env?: Environment): Promise<Act
1372
1389
  if (user) {
1373
1390
  const userStatus = user.lookup('status');
1374
1391
  if (userStatus !== 'Active') {
1375
- throw new UnauthorisedError(`User account is not active. Status: ${userStatus}`);
1392
+ throw new UnauthorisedError(`User account is not active. Status: ${userStatus}`, {
1393
+ agentlangCode: 'AL_AUTH_USER_NOT_ACTIVE_SESSION',
1394
+ });
1376
1395
  }
1377
1396
  }
1378
1397
 
@@ -1382,7 +1401,9 @@ async function verifySessionToken(token: string, env?: Environment): Promise<Act
1382
1401
  return { sessionId: sessId, userId: userId };
1383
1402
  } else {
1384
1403
  logger.warn(`No active session found for user '${userId}'`);
1385
- throw new UnauthorisedError(`No active session for user '${userId}'`);
1404
+ throw new UnauthorisedError(`No active session for user '${userId}'`, {
1405
+ agentlangCode: 'AL_AUTH_NO_ACTIVE_SESSION_TOKEN',
1406
+ });
1386
1407
  }
1387
1408
  } catch (err: any) {
1388
1409
  if (err instanceof UnauthorisedError) {
@@ -1394,7 +1415,9 @@ async function verifySessionToken(token: string, env?: Environment): Promise<Act
1394
1415
  errorMessage: err.message,
1395
1416
  sessionId: sessId,
1396
1417
  });
1397
- throw new UnauthorisedError('Session verification failed');
1418
+ throw new UnauthorisedError('Session verification failed', {
1419
+ agentlangCode: 'AL_AUTH_SESSION_VERIFY_WRAPPER',
1420
+ });
1398
1421
  }
1399
1422
  };
1400
1423
  if (needCommit) {
@@ -167,6 +167,16 @@ entity Migration {
167
167
  downs String @optional
168
168
  }
169
169
 
170
+ // Custom HTTP error messages. \`target\` is "*" for a universal (code-only) override,
171
+ // or "module/Entry" for an entity-specific one. \`message\` may contain the
172
+ // {{code}} and {{message}} placeholders, substituted at resolution time.
173
+ entity errorMessage {
174
+ id UUID @id @default(uuid()),
175
+ target String @default("*") @indexed,
176
+ code String @indexed,
177
+ message String
178
+ }
179
+
170
180
  @public event Query {
171
181
  q Any
172
182
  }
@@ -283,6 +293,22 @@ export async function lookupTimersWithRunningStatus(): Promise<Instance[]> {
283
293
  return await parseAndEvaluateStatement(`{agentlang/timer {status? "R"}}`);
284
294
  }
285
295
 
296
+ /**
297
+ * Look up a custom error-message template stored in the `agentlang/errorMessage`
298
+ * system entity. `target` is "*" for a universal override or "module/Entry" for an
299
+ * entity-specific one. Returns the stored `message`, or undefined when none exists.
300
+ */
301
+ export async function lookupCustomErrorMessage(
302
+ target: string,
303
+ code: string
304
+ ): Promise<string | undefined> {
305
+ const r: any = await parseAndEvaluateStatement(
306
+ `{${DefaultModuleName}/errorMessage {target? "${escapeSpecialChars(target)}", code? "${escapeSpecialChars(code)}"}}`
307
+ );
308
+ const insts: Instance[] = Array.isArray(r) ? r : r ? [r] : [];
309
+ return insts.length > 0 ? insts[0].lookup('message') : undefined;
310
+ }
311
+
286
312
  async function addAudit(
287
313
  env: Environment,
288
314
  action: 'c' | 'd' | 'u',
@@ -45,6 +45,8 @@ import {
45
45
  TenantAttributeName,
46
46
  UnauthorisedError,
47
47
  } from '../../defs.js';
48
+ import { createCodedError } from '../../errors/coded-error.js';
49
+ import { mapDatabaseError } from './db-errors.js';
48
50
  import { saveMigration } from '../../modules/core.js';
49
51
  import { getAppSpec } from '../../loader.js';
50
52
  import { WhereClause } from '../interface.js';
@@ -393,9 +395,10 @@ async function validateSchemaInProd(dataSource: DataSource) {
393
395
  );
394
396
  if (pendingQueries.length > 0) {
395
397
  const pending = pendingQueries.join('\n ');
396
- throw new Error(
398
+ throw createCodedError(
397
399
  `Schema mismatch detected: the app model does not match the database schema. ` +
398
- `Run migrations before starting in production mode.\n Pending changes:\n ${pending}`
400
+ `Run migrations before starting in production mode.\n Pending changes:\n ${pending}`,
401
+ 'AL_DB_SCHEMA_MISMATCH_PROD'
399
402
  );
400
403
  }
401
404
  }
@@ -431,8 +434,9 @@ async function maybeHandleMigrations(dataSource: DataSource) {
431
434
  const simulation = await simulateMigration(dataSource);
432
435
  if (!simulation.success) {
433
436
  logger.error(`Migration simulation failed:\n ${simulation.errors.join('\n ')}`);
434
- throw new Error(
435
- `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`
437
+ throw createCodedError(
438
+ `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`,
439
+ 'AL_DB_MIGRATION_SIMULATION_FAILED'
436
440
  );
437
441
  }
438
442
  logger.info('Migration simulation passed.');
@@ -443,8 +447,9 @@ async function maybeHandleMigrations(dataSource: DataSource) {
443
447
  const simulation = await simulateMigration(dataSource);
444
448
  if (!simulation.success) {
445
449
  logger.error(`Migration simulation failed:\n ${simulation.errors.join('\n ')}`);
446
- throw new Error(
447
- `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`
450
+ throw createCodedError(
451
+ `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`,
452
+ 'AL_DB_MIGRATION_SIMULATION_FAILED'
448
453
  );
449
454
  }
450
455
  logger.info('Migration simulation passed, applying changes...');
@@ -530,7 +535,10 @@ function getDsFunction(
530
535
  case 'sqljs':
531
536
  return makeSqljsDataSource;
532
537
  default:
533
- throw new Error(`Unsupported database type - ${config?.type}`);
538
+ throw createCodedError(
539
+ `Unsupported database type - ${config?.type}`,
540
+ 'AL_DB_UNSUPPORTED_TYPE'
541
+ );
534
542
  }
535
543
  }
536
544
 
@@ -595,7 +603,10 @@ export async function initDatabase(config: DatabaseConfig | undefined) {
595
603
  await initVectorStore(vectEnts, DbContext.getGlobalContext());
596
604
  }
597
605
  } else {
598
- throw new Error(`Unsupported database type - ${getDbType(AppConfig?.store)}`);
606
+ throw createCodedError(
607
+ `Unsupported database type - ${getDbType(AppConfig?.store)}`,
608
+ 'AL_DB_UNSUPPORTED_TYPE_INIT'
609
+ );
599
610
  }
600
611
  }
601
612
  }
@@ -617,9 +628,13 @@ async function insertRowsHelper(
617
628
  ctx: DbContext,
618
629
  doUpsert: boolean
619
630
  ): Promise<void> {
620
- const repo = getDatasourceForTransaction(ctx.txnId).getRepository(tableName);
621
- if (doUpsert) await repo.save(rows);
622
- else await repo.insert(rows);
631
+ try {
632
+ const repo = getDatasourceForTransaction(ctx.txnId).getRepository(tableName);
633
+ if (doUpsert) await repo.save(rows);
634
+ else await repo.insert(rows);
635
+ } catch (err) {
636
+ mapDatabaseError(err);
637
+ }
623
638
  }
624
639
 
625
640
  export async function addRowForFullTextSearch(
@@ -884,7 +899,10 @@ export async function insertRows(
884
899
  }
885
900
  }
886
901
  } else {
887
- throw new UnauthorisedError({ opr: 'insert', entity: tableName });
902
+ throw new UnauthorisedError(
903
+ { opr: 'insert', entity: tableName },
904
+ { agentlangCode: 'AL_DB_INSERT_UNAUTHORIZED' }
905
+ );
888
906
  }
889
907
  }
890
908
 
@@ -926,7 +944,10 @@ export async function insertBetweenRow(
926
944
  const row = Object.fromEntries(attrs);
927
945
  await insertRow(n, row, ctx.clone().setNeedAuthCheck(false), false);
928
946
  } else {
929
- throw new UnauthorisedError({ opr: 'insert', entity: n });
947
+ throw new UnauthorisedError(
948
+ { opr: 'insert', entity: n },
949
+ { agentlangCode: 'AL_DB_BETWEEN_INSERT_UNAUTHORIZED' }
950
+ );
930
951
  }
931
952
  }
932
953
 
@@ -1046,12 +1067,16 @@ export async function updateRow(
1046
1067
  updateObj: object,
1047
1068
  ctx: DbContext
1048
1069
  ): Promise<boolean> {
1049
- await getDatasourceForTransaction(ctx.txnId)
1050
- .createQueryBuilder()
1051
- .update(tableName)
1052
- .set(updateObj)
1053
- .where(objectToWhereClause(queryObj, queryVals), queryVals)
1054
- .execute();
1070
+ try {
1071
+ await getDatasourceForTransaction(ctx.txnId)
1072
+ .createQueryBuilder()
1073
+ .update(tableName)
1074
+ .set(updateObj)
1075
+ .where(objectToWhereClause(queryObj, queryVals), queryVals)
1076
+ .execute();
1077
+ } catch (err) {
1078
+ mapDatabaseError(err);
1079
+ }
1055
1080
  return true;
1056
1081
  }
1057
1082
 
@@ -1069,12 +1094,16 @@ function queryObjectAsWhereClause(qobj: QueryObject): string {
1069
1094
 
1070
1095
  export async function hardDeleteRow(tableName: string, queryObject: QueryObject, ctx: DbContext) {
1071
1096
  const clause = queryObjectAsWhereClause(queryObject);
1072
- await getDatasourceForTransaction(ctx.txnId)
1073
- .createQueryBuilder()
1074
- .delete()
1075
- .from(tableName)
1076
- .where(clause, Object.fromEntries(queryObject))
1077
- .execute();
1097
+ try {
1098
+ await getDatasourceForTransaction(ctx.txnId)
1099
+ .createQueryBuilder()
1100
+ .delete()
1101
+ .from(tableName)
1102
+ .where(clause, Object.fromEntries(queryObject))
1103
+ .execute();
1104
+ } catch (err) {
1105
+ mapDatabaseError(err);
1106
+ }
1078
1107
  return true;
1079
1108
  }
1080
1109
 
@@ -1090,7 +1119,7 @@ function mkBetweenClause(tableName: string | undefined, k: string, queryVals: an
1090
1119
  delete queryVals[k];
1091
1120
  return s;
1092
1121
  } else {
1093
- throw new Error(`between requires an array argument, not ${ov}`);
1122
+ throw createCodedError(`between requires an array argument, not ${ov}`, 'AL_DB_BETWEEN_ARRAY');
1094
1123
  }
1095
1124
  }
1096
1125
 
@@ -1112,7 +1141,10 @@ function objectToWhereClause(queryObj: object, queryVals: any, tableName?: strin
1112
1141
  } else if (op === '<>' || op === '!=') {
1113
1142
  op = 'IS NOT';
1114
1143
  } else {
1115
- throw new Error(`Operator ${op} cannot be appplied to SQL NULL`);
1144
+ throw createCodedError(
1145
+ `Operator ${op} cannot be appplied to SQL NULL`,
1146
+ 'AL_DB_NULL_OPERATOR'
1147
+ );
1116
1148
  }
1117
1149
  }
1118
1150
  const v = isnullcheck ? 'NULL' : `:${k}`;
@@ -1143,7 +1175,10 @@ function objectToRawWhereClause(queryObj: object, queryVals: any, tableName?: st
1143
1175
  } else if (op === '<>' || op === '!=') {
1144
1176
  op = 'IS NOT';
1145
1177
  } else {
1146
- throw new Error(`Operator ${op} cannot be appplied to SQL NULL`);
1178
+ throw createCodedError(
1179
+ `Operator ${op} cannot be appplied to SQL NULL`,
1180
+ 'AL_DB_NULL_OPERATOR'
1181
+ );
1147
1182
  }
1148
1183
  }
1149
1184
  let clause = '';
@@ -1274,8 +1309,12 @@ export async function getMany(
1274
1309
  qb.skip(querySpec.offset);
1275
1310
  }
1276
1311
  qb.where(queryStr, querySpec.queryVals);
1277
- if (hasAggregates) return await qb.getRawMany();
1278
- else return await qb.getMany();
1312
+ try {
1313
+ if (hasAggregates) return await qb.getRawMany();
1314
+ else return await qb.getMany();
1315
+ } catch (err) {
1316
+ mapDatabaseError(err);
1317
+ }
1279
1318
  }
1280
1319
 
1281
1320
  export async function getManyByRawQuery(
@@ -1287,7 +1326,11 @@ export async function getManyByRawQuery(
1287
1326
  .getRepository(tableName)
1288
1327
  .createQueryBuilder();
1289
1328
  qb.where('', querySpec.queryVals);
1290
- return await qb.getMany();
1329
+ try {
1330
+ return await qb.getMany();
1331
+ } catch (err) {
1332
+ mapDatabaseError(err);
1333
+ }
1291
1334
  }
1292
1335
 
1293
1336
  export async function getManyByJoin(
@@ -1348,7 +1391,7 @@ export async function getManyByJoin(
1348
1391
  }
1349
1392
  });
1350
1393
  if (querySpec.intoSpec === undefined) {
1351
- throw new Error('SELECT-INTO pattern is missing');
1394
+ throw createCodedError('SELECT-INTO pattern is missing', 'AL_DB_SELECT_INTO_MISSING');
1352
1395
  }
1353
1396
  const intos = querySpec.intoSpec.size > 0 ? intoSpecToSql(querySpec.intoSpec) : '';
1354
1397
  const intos_sep = intos.length === 0 ? '' : ',';
@@ -1370,7 +1413,11 @@ export async function getManyByJoin(
1370
1413
  }
1371
1414
  logger.debug(`Join Query: ${sql}`);
1372
1415
  const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
1373
- return await qb.query(sql);
1416
+ try {
1417
+ return await qb.query(sql);
1418
+ } catch (err) {
1419
+ mapDatabaseError(err);
1420
+ }
1374
1421
  }
1375
1422
 
1376
1423
  function intoSpecToSql(intoSpec: Map<string, string>): string {
@@ -1440,7 +1487,11 @@ export async function getAllConnected(
1440
1487
  connAlias,
1441
1488
  buildQueryFromConnnectionInfo(connAlias, alias, connInfo)
1442
1489
  );
1443
- return await qb.getRawMany();
1490
+ try {
1491
+ return await qb.getRawMany();
1492
+ } catch (err) {
1493
+ mapDatabaseError(err);
1494
+ }
1444
1495
  }
1445
1496
 
1446
1497
  const transactionsDb: Map<string, QueryRunner> = new Map<string, QueryRunner>();
@@ -1453,7 +1504,7 @@ export async function startDbTransaction(): Promise<string> {
1453
1504
  transactionsDb.set(txnId, queryRunner);
1454
1505
  return txnId;
1455
1506
  } else {
1456
- throw new Error('Database not initialized');
1507
+ throw createCodedError('Database not initialized', 'AL_DB_NOT_INITIALIZED');
1457
1508
  }
1458
1509
  }
1459
1510
 
@@ -1461,13 +1512,13 @@ function getDatasourceForTransaction(txnId: string | undefined): DataSource | En
1461
1512
  if (txnId) {
1462
1513
  const qr: QueryRunner | undefined = transactionsDb.get(txnId);
1463
1514
  if (qr === undefined) {
1464
- throw new Error(`Transaction not found - ${txnId}`);
1515
+ throw createCodedError(`Transaction not found - ${txnId}`, 'AL_DB_TXN_NOT_FOUND');
1465
1516
  } else {
1466
1517
  return qr.manager;
1467
1518
  }
1468
1519
  } else {
1469
1520
  if (defaultDataSource !== undefined) return defaultDataSource;
1470
- else throw new Error('No default datasource is initialized');
1521
+ else throw createCodedError('No default datasource is initialized', 'AL_DB_NO_DATASOURCE');
1471
1522
  }
1472
1523
  }
1473
1524
 
@@ -0,0 +1,113 @@
1
+ import { QueryFailedError } from 'typeorm';
2
+ import { createCodedError, isCodedError } from '../../errors/coded-error.js';
3
+
4
+ /**
5
+ * Normalize TypeORM / driver failures into stable Agentlang codes for HTTP and logging.
6
+ * Preserves the original message text on the thrown Error.
7
+ */
8
+ export function mapDatabaseError(err: unknown): never {
9
+ if (isCodedError(err)) {
10
+ throw err;
11
+ }
12
+
13
+ const anyErr = err as any;
14
+ const msg = anyErr instanceof Error ? anyErr.message : String(err);
15
+ const driver = anyErr?.driverError ?? anyErr;
16
+ const sqlState =
17
+ driver?.code !== undefined && driver?.code !== null
18
+ ? String(driver.code)
19
+ : anyErr?.code !== undefined && anyErr?.code !== null
20
+ ? String(anyErr.code)
21
+ : undefined;
22
+ const errno = driver?.errno ?? anyErr?.errno;
23
+
24
+ // PostgreSQL (SQLSTATE)
25
+ if (sqlState === '23505') {
26
+ throw createCodedError(msg, 'AL_DB_UNIQUE_VIOLATION');
27
+ }
28
+ if (sqlState === '23503') {
29
+ throw createCodedError(msg, 'AL_DB_FOREIGN_KEY_VIOLATION');
30
+ }
31
+ if (sqlState === '23502') {
32
+ throw createCodedError(msg, 'AL_DB_NOT_NULL_VIOLATION');
33
+ }
34
+ if (sqlState === '23514') {
35
+ throw createCodedError(msg, 'AL_DB_CHECK_VIOLATION');
36
+ }
37
+ // Invalid text representation / datetime / numeric range (e.g. non-UUID passed to a UUID column).
38
+ if (sqlState === '22P02' || sqlState === '22007' || sqlState === '22003') {
39
+ throw createCodedError(msg, 'AL_DB_INVALID_TYPE');
40
+ }
41
+ if (sqlState === '40P01') {
42
+ throw createCodedError(msg, 'AL_DB_DEADLOCK');
43
+ }
44
+ if (sqlState === '40001' || sqlState === '25P02') {
45
+ throw createCodedError(msg, 'AL_DB_SERIALIZATION_FAILURE');
46
+ }
47
+
48
+ // MySQL / MariaDB (errno)
49
+ if (errno === 1062) {
50
+ throw createCodedError(msg, 'AL_DB_UNIQUE_VIOLATION');
51
+ }
52
+ if (errno === 1451 || errno === 1452) {
53
+ throw createCodedError(msg, 'AL_DB_FOREIGN_KEY_VIOLATION');
54
+ }
55
+ if (errno === 1048) {
56
+ throw createCodedError(msg, 'AL_DB_NOT_NULL_VIOLATION');
57
+ }
58
+ if (errno === 1213) {
59
+ throw createCodedError(msg, 'AL_DB_DEADLOCK');
60
+ }
61
+ if (errno === 1205) {
62
+ throw createCodedError(msg, 'AL_DB_LOCK_WAIT_TIMEOUT');
63
+ }
64
+ if (errno === 1366 || errno === 1292) {
65
+ throw createCodedError(msg, 'AL_DB_INVALID_TYPE');
66
+ }
67
+
68
+ const m = msg.toLowerCase();
69
+
70
+ // Cross-driver message heuristics (SQLite, sql.js, edge cases)
71
+ if (
72
+ /unique constraint|duplicate key value|duplicate entry|unique failed|constraint failed.*unique/i.test(
73
+ m
74
+ )
75
+ ) {
76
+ throw createCodedError(msg, 'AL_DB_UNIQUE_VIOLATION');
77
+ }
78
+ if (/foreign key constraint|foreign key mismatch|references.*fails/i.test(m)) {
79
+ throw createCodedError(msg, 'AL_DB_FOREIGN_KEY_VIOLATION');
80
+ }
81
+ if (/not-null constraint|null value in column|cannot be null|not null constraint/i.test(m)) {
82
+ throw createCodedError(msg, 'AL_DB_NOT_NULL_VIOLATION');
83
+ }
84
+ if (/check constraint/i.test(m)) {
85
+ throw createCodedError(msg, 'AL_DB_CHECK_VIOLATION');
86
+ }
87
+ if (/deadlock/i.test(m)) {
88
+ throw createCodedError(msg, 'AL_DB_DEADLOCK');
89
+ }
90
+ if (/lock wait timeout|could not obtain lock/i.test(m)) {
91
+ throw createCodedError(msg, 'AL_DB_LOCK_WAIT_TIMEOUT');
92
+ }
93
+ if (/could not serialize|serialization failure/i.test(m)) {
94
+ throw createCodedError(msg, 'AL_DB_SERIALIZATION_FAILURE');
95
+ }
96
+ if (/invalid input syntax|incorrect .* value|out of range|datatype mismatch/i.test(m)) {
97
+ throw createCodedError(msg, 'AL_DB_INVALID_TYPE');
98
+ }
99
+
100
+ if (anyErr instanceof QueryFailedError && /syntax error|parse error/i.test(m)) {
101
+ throw createCodedError(msg, 'AL_DB_SYNTAX_ERROR');
102
+ }
103
+
104
+ if (anyErr instanceof QueryFailedError) {
105
+ throw createCodedError(msg, 'AL_DB_QUERY_FAILED');
106
+ }
107
+
108
+ if (anyErr instanceof Error) {
109
+ throw createCodedError(msg, 'AL_DB_QUERY_FAILED');
110
+ }
111
+
112
+ throw createCodedError(String(err), 'AL_DB_QUERY_FAILED');
113
+ }
@@ -140,6 +140,13 @@ export const ConfigSchema = z.object({
140
140
  })
141
141
  )
142
142
  .optional(),
143
+ customErrorMessages: z
144
+ .object({
145
+ // When true, error responses are resolved against the `agentlang/errorMessage`
146
+ // system entity before falling back to the default message.
147
+ enabled: z.boolean().default(false),
148
+ })
149
+ .optional(),
143
150
  });
144
151
 
145
152
  export type Config = z.infer<typeof ConfigSchema>;
package/src/xlsx.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ declare module 'xlsx' {
2
+ interface WorkSheet {}
3
+ interface WorkBook {
4
+ SheetNames: string[];
5
+ Sheets: { [name: string]: WorkSheet };
6
+ }
7
+ export const utils: {
8
+ book_new(): WorkBook;
9
+ json_to_sheet<T extends object>(data: T[]): WorkSheet;
10
+ aoa_to_sheet(data: unknown[][]): WorkSheet;
11
+ book_append_sheet(workbook: WorkBook, sheet: WorkSheet, name: string): void;
12
+ };
13
+ export function write(
14
+ workbook: WorkBook,
15
+ opts: { type: 'buffer'; bookType: 'xlsx' | 'csv' }
16
+ ): Buffer;
17
+ }