@exabugs/dynamodb-client 1.11.3 → 1.12.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 (67) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/client/Collection.d.ts.map +1 -1
  3. package/dist/client/Collection.js +10 -2
  4. package/dist/client/Collection.js.map +1 -1
  5. package/dist/integrations/react-admin/components/CloudWatchLog/CloudWatchLogButton.d.ts +14 -0
  6. package/dist/integrations/react-admin/components/CloudWatchLog/CloudWatchLogButton.d.ts.map +1 -0
  7. package/dist/integrations/react-admin/components/CloudWatchLog/CloudWatchLogButton.js +17 -0
  8. package/dist/integrations/react-admin/components/CloudWatchLog/CloudWatchLogButton.js.map +1 -0
  9. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.d.ts +32 -0
  10. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.d.ts.map +1 -0
  11. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.js +63 -0
  12. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.js.map +1 -0
  13. package/dist/integrations/react-admin/components/CloudWatchLog/index.d.ts +5 -0
  14. package/dist/integrations/react-admin/components/CloudWatchLog/index.d.ts.map +1 -0
  15. package/dist/integrations/react-admin/components/CloudWatchLog/index.js +3 -0
  16. package/dist/integrations/react-admin/components/CloudWatchLog/index.js.map +1 -0
  17. package/dist/integrations/react-admin/components/index.d.ts +3 -1
  18. package/dist/integrations/react-admin/components/index.d.ts.map +1 -1
  19. package/dist/integrations/react-admin/components/index.js +2 -1
  20. package/dist/integrations/react-admin/components/index.js.map +1 -1
  21. package/dist/integrations/react-admin/index.d.ts +2 -0
  22. package/dist/integrations/react-admin/index.d.ts.map +1 -1
  23. package/dist/integrations/react-admin/index.js +1 -0
  24. package/dist/integrations/react-admin/index.js.map +1 -1
  25. package/dist/notify/alerts.d.ts +145 -0
  26. package/dist/notify/alerts.d.ts.map +1 -0
  27. package/dist/notify/alerts.js +152 -0
  28. package/dist/notify/alerts.js.map +1 -0
  29. package/dist/notify/format.d.ts.map +1 -1
  30. package/dist/notify/format.js +2 -0
  31. package/dist/notify/format.js.map +1 -1
  32. package/dist/notify/handler.d.ts +63 -0
  33. package/dist/notify/handler.d.ts.map +1 -1
  34. package/dist/notify/handler.js +205 -3
  35. package/dist/notify/handler.js.map +1 -1
  36. package/dist/notify/index.d.ts +5 -2
  37. package/dist/notify/index.d.ts.map +1 -1
  38. package/dist/notify/index.js +2 -1
  39. package/dist/notify/index.js.map +1 -1
  40. package/dist/notify/normalize.d.ts +12 -1
  41. package/dist/notify/normalize.d.ts.map +1 -1
  42. package/dist/notify/normalize.js +73 -14
  43. package/dist/notify/normalize.js.map +1 -1
  44. package/dist/notify/store.d.ts +17 -3
  45. package/dist/notify/store.d.ts.map +1 -1
  46. package/dist/notify/store.js +87 -0
  47. package/dist/notify/store.js.map +1 -1
  48. package/dist/notify/types.d.ts +47 -0
  49. package/dist/notify/types.d.ts.map +1 -1
  50. package/dist/server/handler.cjs +105 -114
  51. package/dist/server/handler.cjs.map +3 -3
  52. package/dist/server/media-handler.cjs +2 -2
  53. package/dist/server/media-process-handler-dlq.cjs +2 -2
  54. package/dist/server/media-process-handler-dlq.cjs.map +1 -1
  55. package/dist/server/media-process-handler.cjs +2 -2
  56. package/dist/server/media-process-handler.cjs.map +1 -1
  57. package/dist/server/utils/errorHandler.d.ts +6 -0
  58. package/dist/server/utils/errorHandler.d.ts.map +1 -1
  59. package/dist/server/utils/errorHandler.js +9 -16
  60. package/dist/server/utils/errorHandler.js.map +1 -1
  61. package/dist/server/utils/responseBuilder.d.ts +8 -2
  62. package/dist/server/utils/responseBuilder.d.ts.map +1 -1
  63. package/dist/server/utils/responseBuilder.js +9 -5
  64. package/dist/server/utils/responseBuilder.js.map +1 -1
  65. package/dist/sharp-layer.zip +0 -0
  66. package/docs/NOTIFY.md +81 -1
  67. package/package.json +1 -1
@@ -7,6 +7,12 @@ import type { APIGatewayProxyResultV2 } from 'aws-lambda';
7
7
  /**
8
8
  * エラーをハンドリングしてレスポンスを生成する
9
9
  *
10
+ * ログ出力自体はcreateErrorResponseが一元的に行う(ステータスコードに応じた
11
+ * レベル分け・eventの付与も含む)。ここで個別にログを出すと、同じエラーが
12
+ * 二重に記録されたり、最終的なステータスコードが決まる前にerrorレベルで
13
+ * 出てしまう(4xxなのにerror扱いになる)。例外的にスタックトレースだけは、
14
+ * 原因不明な500の診断に必須なためlogContextとして個別に渡す。
15
+ *
10
16
  * @param error - エラーオブジェクト
11
17
  * @param requestId - リクエストID
12
18
  * @returns HTTPレスポンス
@@ -1 +1 @@
1
- {"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/server/utils/errorHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAe1D;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,uBAAuB,CAsEtF"}
1
+ {"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/server/utils/errorHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAO1D;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,uBAAuB,CAgEtF"}
@@ -1,17 +1,16 @@
1
1
  import { HTTP_STATUS } from '../../shared/constants/http.js';
2
2
  import { VALIDATION_ERROR_PATTERNS } from '../../shared/constants/validation.js';
3
- import { ErrorCode, createLogger, isAppError } from '../../shared/index.js';
3
+ import { ErrorCode, isAppError } from '../../shared/index.js';
4
4
  import { createErrorResponse } from './responseBuilder.js';
5
- /**
6
- * ロガーインスタンス
7
- */
8
- const logger = createLogger({
9
- service: 'error-handler',
10
- level: process.env.LOG_LEVEL || 'info',
11
- });
12
5
  /**
13
6
  * エラーをハンドリングしてレスポンスを生成する
14
7
  *
8
+ * ログ出力自体はcreateErrorResponseが一元的に行う(ステータスコードに応じた
9
+ * レベル分け・eventの付与も含む)。ここで個別にログを出すと、同じエラーが
10
+ * 二重に記録されたり、最終的なステータスコードが決まる前にerrorレベルで
11
+ * 出てしまう(4xxなのにerror扱いになる)。例外的にスタックトレースだけは、
12
+ * 原因不明な500の診断に必須なためlogContextとして個別に渡す。
13
+ *
15
14
  * @param error - エラーオブジェクト
16
15
  * @param requestId - リクエストID
17
16
  * @returns HTTPレスポンス
@@ -23,12 +22,6 @@ export function handleError(error, requestId) {
23
22
  }
24
23
  // 標準Errorの場合
25
24
  if (error instanceof Error) {
26
- // スタックトレースをログ出力
27
- logger.error('Error stack trace', {
28
- requestId,
29
- stack: error.stack,
30
- message: error.message,
31
- });
32
25
  // バリデーションエラーの判定
33
26
  if (isValidationError(error.message)) {
34
27
  return createErrorResponse(ErrorCode.VALIDATION_ERROR, error.message, HTTP_STATUS.BAD_REQUEST, requestId);
@@ -41,8 +34,8 @@ export function handleError(error, requestId) {
41
34
  if (error.message.includes('Unknown operation')) {
42
35
  return createErrorResponse(ErrorCode.INVALID_OPERATION, error.message, HTTP_STATUS.BAD_REQUEST, requestId);
43
36
  }
44
- // その他のエラー
45
- return createErrorResponse(ErrorCode.INTERNAL_ERROR, error.message, HTTP_STATUS.INTERNAL_SERVER_ERROR, requestId);
37
+ // その他のエラー(原因不明の500。スタックトレースをログに残す)
38
+ return createErrorResponse(ErrorCode.INTERNAL_ERROR, error.message, HTTP_STATUS.INTERNAL_SERVER_ERROR, requestId, { stack: error.stack });
46
39
  }
47
40
  // 予期しないエラー
48
41
  return createErrorResponse(ErrorCode.UNKNOWN_ERROR, 'An unexpected error occurred', HTTP_STATUS.INTERNAL_SERVER_ERROR, requestId, {
@@ -1 +1 @@
1
- {"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/server/utils/errorHandler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,GAAG,YAAY,CAAC;IAC1B,OAAO,EAAE,eAAe;IACxB,KAAK,EAAG,OAAO,CAAC,GAAG,CAAC,SAAiD,IAAI,MAAM;CAChF,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,SAAiB;IAC3D,6BAA6B;IAC7B,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,mBAAmB,CACxB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,UAAU,EAChB,SAAS,EACT,KAAK,CAAC,OAAO,CACd,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,gBAAgB;QAChB,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE;YAChC,SAAS;YACT,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QAEH,gBAAgB;QAChB,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO,mBAAmB,CACxB,SAAS,CAAC,gBAAgB,EAC1B,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,WAAW,EACvB,SAAS,CACV,CAAC;QACJ,CAAC;QAED,YAAY;QACZ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAClD,OAAO,mBAAmB,CACxB,SAAS,CAAC,eAAe,EACzB,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,eAAe,EAC3B,SAAS,CACV,CAAC;QACJ,CAAC;QAED,cAAc;QACd,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAChD,OAAO,mBAAmB,CACxB,SAAS,CAAC,iBAAiB,EAC3B,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,WAAW,EACvB,SAAS,CACV,CAAC;QACJ,CAAC;QAED,UAAU;QACV,OAAO,mBAAmB,CACxB,SAAS,CAAC,cAAc,EACxB,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,qBAAqB,EACjC,SAAS,CACV,CAAC;IACJ,CAAC;IAED,WAAW;IACX,OAAO,mBAAmB,CACxB,SAAS,CAAC,aAAa,EACvB,8BAA8B,EAC9B,WAAW,CAAC,qBAAqB,EACjC,SAAS,EACT;QACE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;KACrB,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,OAAe;IACxC,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC"}
1
+ {"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/server/utils/errorHandler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,SAAiB;IAC3D,6BAA6B;IAC7B,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,mBAAmB,CACxB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,UAAU,EAChB,SAAS,EACT,KAAK,CAAC,OAAO,CACd,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,gBAAgB;QAChB,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO,mBAAmB,CACxB,SAAS,CAAC,gBAAgB,EAC1B,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,WAAW,EACvB,SAAS,CACV,CAAC;QACJ,CAAC;QAED,YAAY;QACZ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAClD,OAAO,mBAAmB,CACxB,SAAS,CAAC,eAAe,EACzB,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,eAAe,EAC3B,SAAS,CACV,CAAC;QACJ,CAAC;QAED,cAAc;QACd,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAChD,OAAO,mBAAmB,CACxB,SAAS,CAAC,iBAAiB,EAC3B,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,WAAW,EACvB,SAAS,CACV,CAAC;QACJ,CAAC;QAED,mCAAmC;QACnC,OAAO,mBAAmB,CACxB,SAAS,CAAC,cAAc,EACxB,KAAK,CAAC,OAAO,EACb,WAAW,CAAC,qBAAqB,EACjC,SAAS,EACT,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CACvB,CAAC;IACJ,CAAC;IAED,WAAW;IACX,OAAO,mBAAmB,CACxB,SAAS,CAAC,aAAa,EACvB,8BAA8B,EAC9B,WAAW,CAAC,qBAAqB,EACjC,SAAS,EACT;QACE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;KACrB,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,OAAe;IACxC,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC"}
@@ -15,14 +15,20 @@ export declare function createSuccessResponse(data: unknown, requestId: string):
15
15
  /**
16
16
  * エラーレスポンスを生成する
17
17
  *
18
+ * `logContext`はログにのみ使う(クライアントへのレスポンスには含めない)。
19
+ * 例外オブジェクトは「何が起きたか」の診断情報を持つだけで、それを外部に
20
+ * どう見せるか(レスポンスボディの形)はこの関数の責務であり、呼び出し元の
21
+ * 判断を自動的に継承しない(過去にdetailsを無条件にレスポンスへ転記していた
22
+ * ことがあったが、使われていなかった上に責務が混同されていたため廃止した)。
23
+ *
18
24
  * @param code - エラーコード
19
25
  * @param message - エラーメッセージ
20
26
  * @param statusCode - HTTPステータスコード
21
27
  * @param requestId - リクエストID
22
- * @param details - 追加詳細情報
28
+ * @param logContext - ログにのみ出す診断情報(resource/id等。レスポンスには出ない)
23
29
  * @returns HTTPレスポンス
24
30
  */
25
- export declare function createErrorResponse(code: string, message: string, statusCode: number, requestId: string, details?: unknown): APIGatewayProxyResultV2;
31
+ export declare function createErrorResponse(code: string, message: string, statusCode: number, requestId: string, logContext?: Record<string, unknown>): APIGatewayProxyResultV2;
26
32
  /**
27
33
  * CORSレスポンスを生成する
28
34
  *
@@ -1 +1 @@
1
- {"version":3,"file":"responseBuilder.d.ts","sourceRoot":"","sources":["../../../src/server/utils/responseBuilder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAoB1D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,uBAAuB,CAgB/F;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,GAChB,uBAAuB,CAgCzB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,uBAAuB,CAM9E"}
1
+ {"version":3,"file":"responseBuilder.d.ts","sourceRoot":"","sources":["../../../src/server/utils/responseBuilder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAoB1D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,uBAAuB,CAgB/F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,uBAAuB,CA8BzB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,uBAAuB,CAM9E"}
@@ -37,26 +37,30 @@ export function createSuccessResponse(data, requestId) {
37
37
  /**
38
38
  * エラーレスポンスを生成する
39
39
  *
40
+ * `logContext`はログにのみ使う(クライアントへのレスポンスには含めない)。
41
+ * 例外オブジェクトは「何が起きたか」の診断情報を持つだけで、それを外部に
42
+ * どう見せるか(レスポンスボディの形)はこの関数の責務であり、呼び出し元の
43
+ * 判断を自動的に継承しない(過去にdetailsを無条件にレスポンスへ転記していた
44
+ * ことがあったが、使われていなかった上に責務が混同されていたため廃止した)。
45
+ *
40
46
  * @param code - エラーコード
41
47
  * @param message - エラーメッセージ
42
48
  * @param statusCode - HTTPステータスコード
43
49
  * @param requestId - リクエストID
44
- * @param details - 追加詳細情報
50
+ * @param logContext - ログにのみ出す診断情報(resource/id等。レスポンスには出ない)
45
51
  * @returns HTTPレスポンス
46
52
  */
47
- export function createErrorResponse(code, message, statusCode, requestId, details) {
53
+ export function createErrorResponse(code, message, statusCode, requestId, logContext) {
48
54
  const response = {
49
55
  success: false,
50
56
  error: {
51
57
  code,
52
58
  message,
53
59
  statusCode,
54
- details,
55
60
  },
56
61
  };
57
62
  // 4xx(クライアント起因。存在しないIDの参照など、日常的に起きうる想定内の失敗)はwarn、
58
63
  // 5xx(サーバー起因の障害)はerrorにする。eventにはcode(例: ITEM_NOT_FOUND)を入れる。
59
- // detailsにresource/id等の文脈が入っている場合はログにも残す(原因特定に必須)。
60
64
  // 詳細: docs/NOTIFY.md(eventが無いと通知の重複抑制がIDの数だけ指紋を作ってしまう)
61
65
  const level = statusCode >= 500 ? 'error' : 'warn';
62
66
  logger[level]('Request failed', {
@@ -64,7 +68,7 @@ export function createErrorResponse(code, message, statusCode, requestId, detail
64
68
  event: code,
65
69
  message,
66
70
  statusCode,
67
- ...(details !== undefined && details !== null ? { details } : {}),
71
+ ...(logContext ?? {}),
68
72
  });
69
73
  return {
70
74
  statusCode,
@@ -1 +1 @@
1
- {"version":3,"file":"responseBuilder.js","sourceRoot":"","sources":["../../../src/server/utils/responseBuilder.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,MAAM,MAAM,GAAG,YAAY,CAAC;IAC1B,OAAO,EAAE,kBAAkB;IAC3B,KAAK,EAAG,OAAO,CAAC,GAAG,CAAC,SAAiD,IAAI,MAAM;CAChF,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa,EAAE,SAAiB;IACpE,MAAM,QAAQ,GAAgC;QAC5C,OAAO,EAAE,IAAI;QACb,IAAI;KACL,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAEhD,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,EAAE;QAC1B,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,YAAY;SAChB;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,OAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,OAAiB;IAEjB,MAAM,QAAQ,GAAqB;QACjC,OAAO,EAAE,KAAK;QACd,KAAK,EAAE;YACL,IAAI;YACJ,OAAO;YACP,UAAU;YACV,OAAO;SACR;KACF,CAAC;IAEF,kDAAkD;IAClD,8DAA8D;IAC9D,mDAAmD;IACnD,uDAAuD;IACvD,MAAM,KAAK,GAAG,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,CAAC,gBAAgB,EAAE;QAC9B,SAAS;QACT,KAAK,EAAE,IAAI;QACX,OAAO;QACP,UAAU;QACV,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClE,CAAC,CAAC;IAEH,OAAO;QACL,UAAU;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,YAAY;SAChB;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,OAAO;QACL,UAAU;QACV,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,EAAE;KACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"responseBuilder.js","sourceRoot":"","sources":["../../../src/server/utils/responseBuilder.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,MAAM,MAAM,GAAG,YAAY,CAAC;IAC1B,OAAO,EAAE,kBAAkB;IAC3B,KAAK,EAAG,OAAO,CAAC,GAAG,CAAC,SAAiD,IAAI,MAAM;CAChF,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa,EAAE,SAAiB;IACpE,MAAM,QAAQ,GAAgC;QAC5C,OAAO,EAAE,IAAI;QACb,IAAI;KACL,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAEhD,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,EAAE;QAC1B,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,YAAY;SAChB;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,OAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,UAAoC;IAEpC,MAAM,QAAQ,GAAqB;QACjC,OAAO,EAAE,KAAK;QACd,KAAK,EAAE;YACL,IAAI;YACJ,OAAO;YACP,UAAU;SACX;KACF,CAAC;IAEF,kDAAkD;IAClD,8DAA8D;IAC9D,uDAAuD;IACvD,MAAM,KAAK,GAAG,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,CAAC,gBAAgB,EAAE;QAC9B,SAAS;QACT,KAAK,EAAE,IAAI;QACX,OAAO;QACP,UAAU;QACV,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;KACtB,CAAC,CAAC;IAEH,OAAO;QACL,UAAU;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,YAAY;SAChB;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,OAAO;QACL,UAAU;QACV,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,EAAE;KACT,CAAC;AACJ,CAAC"}
Binary file
package/docs/NOTIFY.md CHANGED
@@ -24,7 +24,7 @@ JSON 1 行。`level`(`debug` / `info` / `warn` / `error` / `fatal`)、`messa
24
24
 
25
25
  ## Notice
26
26
 
27
- 外部へ送るのは、許可したフィールドだけ(`level` / `event` / `message` / `app` / `env` / `target` / `action` / `occurredAt` / `suppressed`)。文字数を制限し、制御文字を除く。ログ全体は転送しない。
27
+ 外部へ送るのは、許可したフィールドだけ(`level` / `event` / `message` / `app` / `env` / `target` / `action` / `occurredAt` / `suppressed`。alerts を有効にしたときは `detailUrl` も)。文字数を制限し、制御文字を除く。ログ全体は転送しない。
28
28
 
29
29
  ## 重複抑制と流量制限
30
30
 
@@ -101,3 +101,83 @@ export const handler = createNotifyHandler({
101
101
  - アラームは、ログを出さないもの(Lambda の Errors / Throttles、DynamoDB のスロットル、WAF など)に使う。ログと同じ事象(SPARSE_SCAN など)をアラームでも通知すると二重になる
102
102
  - Lambda の非同期の再試行は 0 にする(重複を避ける。失敗は失敗先で受ける)
103
103
  - SES の送信元ドメインは、検証と本番アクセスが済んだリージョンのものを使う
104
+
105
+ ## alerts(通知を管理画面のレコードとして残す)
106
+
107
+ 任意の機能([ADR 0003](adr/0003-alerts.md))。`recorder` を渡したときだけ動き、渡さなければ従来と何も変わらない。通知を、指紋ごとに 1 件の `alerts` レコード(呼び出し側の Single-Table に相乗り)として残し、通知の本文に `詳細: <adminBaseUrl>/alerts/<id>/show` を足す。管理者は AWS にログインせず、レコードの `level` / `event` / `message` / `target` / `action` / `count` / `firstSeenAt` / `lastSeenAt` で対処できる。ログの購読から来た通知は、`firstLog` / `lastLog`(`region` / `logGroup` / `logStream` / `timestamp` / `requestId?`)も持つ。`requestId` は Lambda のもの(テキスト形式の接頭辞・JSON 形式の外側・`RequestId:` 行)だけで、接頭辞の無いアプリの JSON 行の `requestId` は使わない。
108
+
109
+ - 記録は重複抑制の前に行い、抑制された発生も数える。アラームの ALARM → OK は同じレコード(`state`)
110
+ - ID と件数の正は、通知ストアの `al#<alertKey>` 行(`UpdateItem` 1 回で原子的に更新)。レコードの `count` はその写しで、目安
111
+ - 記録の単位は alertKey(同じ呼び出しの中の、同じ alertKey の発生は 1 つにまとめる)。結果の `recorded` / `unrecorded` も、通知の件数ではなく、この単位の数
112
+ - 記録の上限: 3 秒・50 単位(error / fatal を優先)・並列 5(`recordLimits` で変更可。有限の正の整数でない値は既定値)。超えた分・失敗した分・遮断中の分は `unrecorded` に数え、その通知は URL なしで送る。記録の失敗で通知は止まらない。`dryRun` では記録しない
113
+ - 記録しないもの: `target` が `alerts` の通知、`HEARTBEAT`(`skipRecordEvents` で変更可)、`skipRecordLogGroups` に当たるロググループから来た通知(通知は送る。`unrecorded` にも数えない)
114
+ - `adminBaseUrl` が `http://` / `https://` で始まらなければ、URL を付けない(記録はする。起動時に warn を 1 回出す)。末尾の `/` は除く
115
+
116
+ #### ループを防ぐ(Records のロググループを `skipRecordLogGroups` に入れる)
117
+
118
+ 記録先の Records のログを購読していると、記録の失敗(Records の 500)→ Records のエラーログ → 通知 → 記録 → 失敗、が回る。Records の失敗ログ(`Request failed` の `INTERNAL_ERROR`、`Failed to prepare record for update` など)には `resource` が出ないので、`target` が `alerts` かどうかでは見分けられない(`target` での除外は、`resource: 'alerts'` を出すログにしか効かない)。
119
+
120
+ - **除外(必ず設定する)**: Records のロググループを `skipRecordLogGroups` に入れる(完全一致、または末尾 `*` の前方一致)。そのロググループから来た通知は記録しない(通知は従来どおり送る)。Records 自身の障害は通知で届くが、alerts には残らない
121
+ - **遮断(最後の防壁。設定不要)**: 記録の失敗・時間切れが 3 回続いたら、そのハンドラ(コンテナ)では 60 秒間記録しない(`recordBreaker: { failures, cooldownMs }` で変更可)。遮断中も通知は URL なしで送り、結果に `recordSuspended: true` が入る。遮断した時に warn を 1 回出す。期間が過ぎた後の最初の失敗で再び遮断し、成功で解除する。状態はコンテナごとなので、複数のコンテナが動いていれば、それぞれが 3 回まで試す
122
+ - **`message` / `target` は 28 日間保存される**(秘密・個人情報は伏せない。`clean()` は制御文字と長さだけ)。ログに秘密を出さないこと
123
+
124
+ ### 配線(各アプリ)
125
+
126
+ ```ts
127
+ import { DynamoClient } from '@exabugs/dynamodb-client/client/iam';
128
+ import {
129
+ DynamoNoticeStore,
130
+ createAlertRecorder,
131
+ createNotifyHandler,
132
+ } from '@exabugs/dynamodb-client/notify';
133
+
134
+ const store = new DynamoNoticeStore(doc, process.env.NOTICE_TABLE!);
135
+ // timeout は記録の上限(3 秒)以下にする(打ち切った後に HTTP が残らないよう、短くする)
136
+ const records = new DynamoClient(process.env.RECORDS_IAM_URL!, { auth: { region }, timeout: 2500 });
137
+ await records.connect(); // 呼び出しのたびに作るなら、管理者の一覧の取得(admins.ts)と同じ形でよい
138
+
139
+ export const handler = createNotifyHandler({
140
+ // …従来の設定…
141
+ store,
142
+ recorder: createAlertRecorder({ collection: records.db().collection('alerts'), index: store }),
143
+ adminBaseUrl: process.env.ADMIN_BASE_URL, // 例: https://admin.example.com(BrowserRouter)
144
+ // Records(alerts の書き込み先)のロググループ。ループを防ぐ(上の「ループを防ぐ」)
145
+ skipRecordLogGroups: ['/aws/lambda/astro-dev-records'], // 例。各アプリの Records 関数のロググループ
146
+ });
147
+ ```
148
+
149
+ - 環境変数: 既存の `NOTICE_TABLE` / `RECORDS_IAM_URL` に加えて、`ADMIN_BASE_URL`(任意。無ければ URL を載せない)
150
+ - IAM: 通知ストアへは `dynamodb:UpdateItem` だけ(既存の権限のまま。`GetItem` は要らない)。Records の IAM URL の呼び出し(`lambda:InvokeFunctionUrl`)は、管理者の一覧の取得で既に持っている
151
+ - 同時実行数: 同じレコードへの同時の書き込みはシャドーを孤立させうるので、**記録を有効にするときだけ**、通知 Lambda の予約同時実行数を 1 にする(今の通知 Lambda は未設定。入れると、記録なしでも遅れ方が変わる)。直列化は努力目標で、強制はされない(打ち切った HTTP は止まらない・削除の定時処理と重なる・管理画面から書ける)。孤立したシャドーは `src/scripts/repair-shadows.ts` で修復できる
152
+ - ライブラリは Terraform を提供しない(`alerts` の定時削除・同時実行数・環境変数は各アプリの infra で)
153
+
154
+ ### 削除(定時 Lambda)
155
+
156
+ ネイティブ TTL は効かない(`ttl` が最上位の属性にならない)。アプリの定時 Lambda(1 日 1 回など)から呼ぶ:
157
+
158
+ ```ts
159
+ import { DynamoClient } from '@exabugs/dynamodb-client/client/iam';
160
+ import { cleanupAlerts } from '@exabugs/dynamodb-client/notify';
161
+
162
+ export const handler = async () => {
163
+ const client = new DynamoClient(process.env.RECORDS_IAM_URL!, { auth: { region } });
164
+ await client.connect();
165
+ try {
166
+ const r = await cleanupAlerts({ collection: client.db().collection('alerts'), maxPerRun: 500 });
167
+ if (r.errors.length > 0) throw new Error(`alerts の削除に失敗: ${r.errors.join(' / ')}`);
168
+ return r; // more: true なら、次回に残りを消す
169
+ } finally {
170
+ await client.close();
171
+ }
172
+ };
173
+ ```
174
+
175
+ - `lastSeenAt` の昇順に条件なしで読み、期限内のものが出たら止める(`lastSeenAt: { $lt }` の範囲検索は、本体の不具合で `ValidationException` になるため。TODO.md)
176
+ - 読んだ ID は直ちに `deleteMany({ id: { $in } })` で消す(本体とシャドーを一緒に消す)。存在しない ID の削除は Records が warn を出す(それも通知に戻る)
177
+ - 失敗は `errors` / `failed` で返す(握りつぶさない)
178
+
179
+ ### 管理画面(各アプリ)
180
+
181
+ - 一覧・詳細はアプリが書く。`<Datagrid bulkActionButtons={false}>`、Edit なしの読み取り専用にする。ただし読み取り専用は UI だけの担保(Records 本体はリソースごとの認可を持たない)。管理者以外から `alerts` を読めないことは、各アプリの権限で確認する
182
+ - URL は `<adminBaseUrl>/alerts/<id>/show`(BrowserRouter)。未ログインで開くと、ログイン後にダッシュボードへ飛んで遷移先が消えるので、各アプリで元の URL に戻す処理が要る
183
+ - 詳細に `CloudWatchLogButton`(`@exabugs/dynamodb-client/integrations/react-admin`)を置ける。AWS を操作できる人向けの補助で、ログ参照(既定 `lastLog`、`source="firstLog"` も可)が無ければ何も描画しない。リージョン・ドメイン・時間幅(前後 5 分)は部品が決める。商用・中国(`cn-*`)・GovCloud(`us-gov-*`)以外のリージョン(ISO など)や形の不正なリージョンでは、何も描画しない。URL の形式は、ap-northeast-1 の実機のコンソールで確認済み。`aws-cn` / `aws-us-gov` のドメインは未確認(ADR 0003)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exabugs/dynamodb-client",
3
- "version": "1.11.3",
3
+ "version": "1.12.0",
4
4
  "description": "DynamoDB Single-Table Client SDK with MongoDB-like API, Shadow Records, and Lambda implementation for serverless applications",
5
5
  "author": "exabugs",
6
6
  "license": "MIT",