@exabugs/dynamodb-client 1.18.0 → 1.20.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 (113) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +1 -1
  3. package/bin/dynamodb-system-change.js +6 -0
  4. package/dist/integrations/react-admin/components/Alerts/AlertsList.d.ts +18 -0
  5. package/dist/integrations/react-admin/components/Alerts/AlertsList.d.ts.map +1 -0
  6. package/dist/integrations/react-admin/components/Alerts/AlertsList.js +34 -0
  7. package/dist/integrations/react-admin/components/Alerts/AlertsList.js.map +1 -0
  8. package/dist/integrations/react-admin/components/Alerts/AlertsShow.d.ts +10 -0
  9. package/dist/integrations/react-admin/components/Alerts/AlertsShow.d.ts.map +1 -0
  10. package/dist/integrations/react-admin/components/Alerts/AlertsShow.js +24 -0
  11. package/dist/integrations/react-admin/components/Alerts/AlertsShow.js.map +1 -0
  12. package/dist/integrations/react-admin/components/Alerts/alertsResource.d.ts +18 -0
  13. package/dist/integrations/react-admin/components/Alerts/alertsResource.d.ts.map +1 -0
  14. package/dist/integrations/react-admin/components/Alerts/alertsResource.js +19 -0
  15. package/dist/integrations/react-admin/components/Alerts/alertsResource.js.map +1 -0
  16. package/dist/integrations/react-admin/components/Alerts/alertsView.d.ts +51 -0
  17. package/dist/integrations/react-admin/components/Alerts/alertsView.d.ts.map +1 -0
  18. package/dist/integrations/react-admin/components/Alerts/alertsView.js +35 -0
  19. package/dist/integrations/react-admin/components/Alerts/alertsView.js.map +1 -0
  20. package/dist/integrations/react-admin/components/Alerts/index.d.ts +8 -0
  21. package/dist/integrations/react-admin/components/Alerts/index.d.ts.map +1 -0
  22. package/dist/integrations/react-admin/components/Alerts/index.js +5 -0
  23. package/dist/integrations/react-admin/components/Alerts/index.js.map +1 -0
  24. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.d.ts +16 -0
  25. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.d.ts.map +1 -1
  26. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.js +27 -3
  27. package/dist/integrations/react-admin/components/CloudWatchLog/cloudWatchLogUrl.js.map +1 -1
  28. package/dist/integrations/react-admin/components/CloudWatchLog/index.d.ts +2 -2
  29. package/dist/integrations/react-admin/components/CloudWatchLog/index.d.ts.map +1 -1
  30. package/dist/integrations/react-admin/components/CloudWatchLog/index.js +1 -1
  31. package/dist/integrations/react-admin/components/CloudWatchLog/index.js.map +1 -1
  32. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesList.d.ts +29 -0
  33. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesList.d.ts.map +1 -0
  34. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesList.js +38 -0
  35. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesList.js.map +1 -0
  36. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesShow.d.ts +9 -0
  37. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesShow.d.ts.map +1 -0
  38. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesShow.js +50 -0
  39. package/dist/integrations/react-admin/components/SystemChanges/SystemChangesShow.js.map +1 -0
  40. package/dist/integrations/react-admin/components/SystemChanges/index.d.ts +8 -0
  41. package/dist/integrations/react-admin/components/SystemChanges/index.d.ts.map +1 -0
  42. package/dist/integrations/react-admin/components/SystemChanges/index.js +5 -0
  43. package/dist/integrations/react-admin/components/SystemChanges/index.js.map +1 -0
  44. package/dist/integrations/react-admin/components/SystemChanges/systemChangesResource.d.ts +19 -0
  45. package/dist/integrations/react-admin/components/SystemChanges/systemChangesResource.d.ts.map +1 -0
  46. package/dist/integrations/react-admin/components/SystemChanges/systemChangesResource.js +20 -0
  47. package/dist/integrations/react-admin/components/SystemChanges/systemChangesResource.js.map +1 -0
  48. package/dist/integrations/react-admin/components/SystemChanges/systemChangesView.d.ts +96 -0
  49. package/dist/integrations/react-admin/components/SystemChanges/systemChangesView.d.ts.map +1 -0
  50. package/dist/integrations/react-admin/components/SystemChanges/systemChangesView.js +99 -0
  51. package/dist/integrations/react-admin/components/SystemChanges/systemChangesView.js.map +1 -0
  52. package/dist/integrations/react-admin/components/index.d.ts +7 -3
  53. package/dist/integrations/react-admin/components/index.d.ts.map +1 -1
  54. package/dist/integrations/react-admin/components/index.js +5 -2
  55. package/dist/integrations/react-admin/components/index.js.map +1 -1
  56. package/dist/integrations/react-admin/components/shared/LabeledIf.d.ts +23 -0
  57. package/dist/integrations/react-admin/components/shared/LabeledIf.d.ts.map +1 -0
  58. package/dist/integrations/react-admin/components/shared/LabeledIf.js +13 -0
  59. package/dist/integrations/react-admin/components/shared/LabeledIf.js.map +1 -0
  60. package/dist/integrations/react-admin/components/shared/messages.d.ts +9 -0
  61. package/dist/integrations/react-admin/components/shared/messages.d.ts.map +1 -0
  62. package/dist/integrations/react-admin/components/shared/messages.js +9 -0
  63. package/dist/integrations/react-admin/components/shared/messages.js.map +1 -0
  64. package/dist/integrations/react-admin/index.d.ts +6 -2
  65. package/dist/integrations/react-admin/index.d.ts.map +1 -1
  66. package/dist/integrations/react-admin/index.js +5 -1
  67. package/dist/integrations/react-admin/index.js.map +1 -1
  68. package/dist/server/handler.cjs +3 -3
  69. package/dist/server/media-handler.cjs +2 -2
  70. package/dist/server/media-process-handler-dlq.cjs +2 -2
  71. package/dist/server/media-process-handler.cjs +2 -2
  72. package/dist/system-changes/index.d.ts +2 -0
  73. package/dist/system-changes/index.d.ts.map +1 -1
  74. package/dist/system-changes/index.js +1 -0
  75. package/dist/system-changes/index.js.map +1 -1
  76. package/dist/system-changes/labels.d.ts +23 -0
  77. package/dist/system-changes/labels.d.ts.map +1 -0
  78. package/dist/system-changes/labels.js +32 -0
  79. package/dist/system-changes/labels.js.map +1 -0
  80. package/dist/system-changes/record.d.ts +1 -1
  81. package/dist/system-changes/record.d.ts.map +1 -1
  82. package/dist/system-changes/record.js +1 -1
  83. package/dist/system-changes/record.js.map +1 -1
  84. package/dist/system-changes/slack.d.ts.map +1 -1
  85. package/dist/system-changes/slack.js +6 -27
  86. package/dist/system-changes/slack.js.map +1 -1
  87. package/dist/tools/dynamodb-system-change/cli.d.ts +35 -0
  88. package/dist/tools/dynamodb-system-change/cli.d.ts.map +1 -0
  89. package/dist/tools/dynamodb-system-change/cli.js +226 -0
  90. package/dist/tools/dynamodb-system-change/cli.js.map +1 -0
  91. package/dist/tools/dynamodb-system-change/details.d.ts +42 -0
  92. package/dist/tools/dynamodb-system-change/details.d.ts.map +1 -0
  93. package/dist/tools/dynamodb-system-change/details.js +59 -0
  94. package/dist/tools/dynamodb-system-change/details.js.map +1 -0
  95. package/dist/tools/dynamodb-system-change/index.d.ts +13 -0
  96. package/dist/tools/dynamodb-system-change/index.d.ts.map +1 -0
  97. package/dist/tools/dynamodb-system-change/index.js +9 -0
  98. package/dist/tools/dynamodb-system-change/index.js.map +1 -0
  99. package/dist/tools/dynamodb-system-change/releases.d.ts +34 -0
  100. package/dist/tools/dynamodb-system-change/releases.d.ts.map +1 -0
  101. package/dist/tools/dynamodb-system-change/releases.js +108 -0
  102. package/dist/tools/dynamodb-system-change/releases.js.map +1 -0
  103. package/dist/tools/dynamodb-system-change/run.d.ts +2 -0
  104. package/dist/tools/dynamodb-system-change/run.d.ts.map +1 -0
  105. package/dist/tools/dynamodb-system-change/run.js +6 -0
  106. package/dist/tools/dynamodb-system-change/run.js.map +1 -0
  107. package/dist/tools/dynamodb-system-change/send.d.ts +43 -0
  108. package/dist/tools/dynamodb-system-change/send.d.ts.map +1 -0
  109. package/dist/tools/dynamodb-system-change/send.js +97 -0
  110. package/dist/tools/dynamodb-system-change/send.js.map +1 -0
  111. package/docs/NOTIFY.md +1 -1
  112. package/docs/SYSTEM_CHANGES.md +69 -0
  113. package/package.json +15 -2
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `dynamodb-system-change`(terraform の apply・destroy のシステム変更履歴への記録の CLI。ADR 0006 の段階 5)の部品。
3
+ * 使い方は docs/SYSTEM_CHANGES.md の「記録の CLI」
4
+ */
5
+ export { USAGE, finishDetailsOf, main, parseRecordArgs } from './cli.js';
6
+ export type { CliDeps, CliIo, RecordArgs } from './cli.js';
7
+ export { DEFAULT_RELEASES_MAX_BYTES, buildFinishDetails, limitReleases, resourceCountsOf, } from './details.js';
8
+ export type { FinishDetails, ReleasesLimit } from './details.js';
9
+ export { EXTRACT_MAX_ITEMS, extractLambdaReleases, splitLines, stripAnsi } from './releases.js';
10
+ export type { ExtractedLambdaRelease } from './releases.js';
11
+ export { actorOf, commitOf, pythonJsonDumps, runCommand, sendSystemChange } from './send.js';
12
+ export type { RunCommand, SendOptions, SystemChangePayload } from './send.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACzE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAChG,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `dynamodb-system-change`(terraform の apply・destroy のシステム変更履歴への記録の CLI。ADR 0006 の段階 5)の部品。
3
+ * 使い方は docs/SYSTEM_CHANGES.md の「記録の CLI」
4
+ */
5
+ export { USAGE, finishDetailsOf, main, parseRecordArgs } from './cli.js';
6
+ export { DEFAULT_RELEASES_MAX_BYTES, buildFinishDetails, limitReleases, resourceCountsOf, } from './details.js';
7
+ export { EXTRACT_MAX_ITEMS, extractLambdaReleases, splitLines, stripAnsi } from './releases.js';
8
+ export { actorOf, commitOf, pythonJsonDumps, runCommand, sendSystemChange } from './send.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEhG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * terraform の plan / apply のログから、Lambda のコードのリリース(作成・置き換え・コードの更新)の成功・失敗を取り出す。
3
+ *
4
+ * asanowa(ADR 0044)・astro(ADR-0011)の `infra/scripts/parse-lambda-releases.py` を Node に移植したもの
5
+ * (両アプリの版は、ロジックが同一。違いは docstring だけ)。出力は Python 版と同じ(テストで固定)。
6
+ *
7
+ * 取り出す条件(Terraform 1.x と AWS プロバイダ 6.x の実際の出力で確認済み):
8
+ * - 計画の `# <アドレス> will be created / must be replaced / will be replaced, as requested / will be updated in-place`
9
+ * のうち、aws_lambda_function で、作成・置き換え、または更新で `source_code_hash` の差分があるもの(= コードが変わる)。
10
+ * 設定だけの更新(環境変数・同時実行数など)は、対象外
11
+ * - 成功: `<アドレス>: Modifications complete after … [id=<関数名>]`(更新)/ `Creation complete after … [id=<関数名>]`(作成)
12
+ * - 失敗: `Error: creating Lambda Function (<名前>)` / `Error: updating Lambda Function (<名前>) code` /
13
+ * `Error: waiting for Lambda Function (<名前>) code update|create`(設定の更新の失敗は、対象外)
14
+ */
15
+ /** 取り出した 1 件(`details.lambdaReleases` の 1 件と同じ形) */
16
+ export interface ExtractedLambdaRelease {
17
+ target: string;
18
+ result: 'succeeded' | 'failed';
19
+ change: 'create' | 'replace' | 'code';
20
+ }
21
+ /** 取り出す上限(Python 版の MAX_ITEMS) */
22
+ export declare const EXTRACT_MAX_ITEMS = 50;
23
+ /** 色コード(Python 版の ANSI) */
24
+ export declare const ANSI_PATTERN: RegExp;
25
+ /**
26
+ * Python の `str.splitlines()` と同じ区切りで行に分ける(\r\n、\n、\r、\v、\f、\x1c〜\x1e、\x85、U+2028、U+2029)。
27
+ * 末尾の区切りの後ろに空の行を作らない
28
+ */
29
+ export declare function splitLines(text: string): string[];
30
+ /** 色コードを除く */
31
+ export declare function stripAnsi(text: string): string;
32
+ /** ログから Lambda のリリースを取り出す(該当が無ければ空の配列。最大 EXTRACT_MAX_ITEMS 件) */
33
+ export declare function extractLambdaReleases(text: string): ExtractedLambdaRelease[];
34
+ //# sourceMappingURL=releases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"releases.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/releases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,oDAAoD;AACpD,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;CACvC;AAED,kCAAkC;AAClC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,2BAA2B;AAC3B,eAAO,MAAM,YAAY,QAAoB,CAAC;AAsB9C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAIjD;AAED,cAAc;AACd,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAoCD,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,EAAE,CA2B5E"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * terraform の plan / apply のログから、Lambda のコードのリリース(作成・置き換え・コードの更新)の成功・失敗を取り出す。
3
+ *
4
+ * asanowa(ADR 0044)・astro(ADR-0011)の `infra/scripts/parse-lambda-releases.py` を Node に移植したもの
5
+ * (両アプリの版は、ロジックが同一。違いは docstring だけ)。出力は Python 版と同じ(テストで固定)。
6
+ *
7
+ * 取り出す条件(Terraform 1.x と AWS プロバイダ 6.x の実際の出力で確認済み):
8
+ * - 計画の `# <アドレス> will be created / must be replaced / will be replaced, as requested / will be updated in-place`
9
+ * のうち、aws_lambda_function で、作成・置き換え、または更新で `source_code_hash` の差分があるもの(= コードが変わる)。
10
+ * 設定だけの更新(環境変数・同時実行数など)は、対象外
11
+ * - 成功: `<アドレス>: Modifications complete after … [id=<関数名>]`(更新)/ `Creation complete after … [id=<関数名>]`(作成)
12
+ * - 失敗: `Error: creating Lambda Function (<名前>)` / `Error: updating Lambda Function (<名前>) code` /
13
+ * `Error: waiting for Lambda Function (<名前>) code update|create`(設定の更新の失敗は、対象外)
14
+ */
15
+ /** 取り出す上限(Python 版の MAX_ITEMS) */
16
+ export const EXTRACT_MAX_ITEMS = 50;
17
+ /** 色コード(Python 版の ANSI) */
18
+ export const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
19
+ const ADDR = String.raw `\S*aws_lambda_function\.[A-Za-z0-9_]+(?:\[[^\]\n]*\])?`;
20
+ // 見出しの言い方: will be created / will be updated in-place / must be replaced / will be replaced, as requested(-replace)/
21
+ // is tainted, so must be replaced / will be destroyed
22
+ const HEADER = new RegExp(String.raw `^\s*# (` +
23
+ ADDR +
24
+ String.raw `) (will be created|will be updated in-place|will be destroyed|` +
25
+ String.raw `(?:is tainted, so )?must be replaced|will be replaced, as requested)`);
26
+ const ANY_HEADER = /^\s*# \S+ (?:will|must|is) /;
27
+ const DONE = new RegExp('^(' + ADDR + String.raw `): (Modifications|Creation) complete after \S+ \[id=([^\]\s]+)\]`);
28
+ const FAILED = [
29
+ [/Error: creating Lambda Function \(([^)\s]+)\)/, 'create'],
30
+ [/Error: updating Lambda Function \(([^)\s]+)\) code/, 'code'],
31
+ [/Error: waiting for Lambda Function \(([^)\s]+)\) code update/, 'code'],
32
+ [/Error: waiting for Lambda Function \(([^)\s]+)\) create/, 'create'],
33
+ ];
34
+ /**
35
+ * Python の `str.splitlines()` と同じ区切りで行に分ける(\r\n、\n、\r、\v、\f、\x1c〜\x1e、\x85、U+2028、U+2029)。
36
+ * 末尾の区切りの後ろに空の行を作らない
37
+ */
38
+ export function splitLines(text) {
39
+ const lines = text.split(/\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/);
40
+ if (lines.length > 0 && lines[lines.length - 1] === '')
41
+ lines.pop();
42
+ return lines;
43
+ }
44
+ /** 色コードを除く */
45
+ export function stripAnsi(text) {
46
+ return text.replace(ANSI_PATTERN, '');
47
+ }
48
+ /** 計画の見出しから「コードが変わる Lambda のアドレス → 変更の種類」を作る */
49
+ function codeChangesOf(lines) {
50
+ const changes = new Map();
51
+ let i = 0;
52
+ while (i < lines.length) {
53
+ const m = HEADER.exec(lines[i]);
54
+ if (!m) {
55
+ i += 1;
56
+ continue;
57
+ }
58
+ const [, addr, action] = m;
59
+ let j = i + 1;
60
+ while (j < lines.length &&
61
+ !ANY_HEADER.test(lines[j]) &&
62
+ !lines[j].startsWith('Plan:') &&
63
+ !lines[j].startsWith('Do you want')) {
64
+ j += 1;
65
+ }
66
+ const block = lines.slice(i + 1, j);
67
+ if (action === 'will be created')
68
+ changes.set(addr, 'create');
69
+ else if (action.includes('replaced'))
70
+ changes.set(addr, 'replace');
71
+ else if (action === 'will be updated in-place' &&
72
+ block.some((l) => l.includes('source_code_hash'))) {
73
+ changes.set(addr, 'code');
74
+ }
75
+ i = j;
76
+ }
77
+ return changes;
78
+ }
79
+ /** ログから Lambda のリリースを取り出す(該当が無ければ空の配列。最大 EXTRACT_MAX_ITEMS 件) */
80
+ export function extractLambdaReleases(text) {
81
+ const lines = splitLines(text).map(stripAnsi);
82
+ const codeChanges = codeChangesOf(lines);
83
+ const out = [];
84
+ const seen = new Set();
85
+ for (const raw of lines) {
86
+ const line = raw.replace(/^[│ ]+/, '').trimEnd();
87
+ const done = DONE.exec(line);
88
+ const change = done ? codeChanges.get(done[1]) : undefined;
89
+ if (done && change) {
90
+ const name = done[3];
91
+ if (!seen.has(name)) {
92
+ seen.add(name);
93
+ out.push({ target: name, result: 'succeeded', change });
94
+ }
95
+ continue;
96
+ }
97
+ for (const [rx, failedChange] of FAILED) {
98
+ const m = rx.exec(line);
99
+ if (m && !seen.has(m[1])) {
100
+ seen.add(m[1]);
101
+ out.push({ target: m[1], result: 'failed', change: failedChange });
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ return out.slice(0, EXTRACT_MAX_ITEMS);
107
+ }
108
+ //# sourceMappingURL=releases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"releases.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/releases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/run.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { main } from './cli.js';
2
+ process.exitCode = main(process.argv.slice(2), {
3
+ out: (s) => process.stdout.write(s),
4
+ err: (s) => process.stderr.write(s),
5
+ });
6
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IAC7C,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;CACpC,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ /** 外部コマンドの実行(テストで差し替える) */
2
+ export type RunCommand = (command: string, args: string[], options?: {
3
+ cwd?: string;
4
+ }) => {
5
+ status: number | null;
6
+ stdout: Buffer;
7
+ stderr: Buffer;
8
+ error?: Error;
9
+ };
10
+ export declare const runCommand: RunCommand;
11
+ /** 記録 Lambda に送る本文(記録 Lambda の入力。キーの順は現行と同じ) */
12
+ export interface SystemChangePayload {
13
+ kind: string;
14
+ result: 'started' | 'succeeded' | 'failed';
15
+ target: string;
16
+ commit: string;
17
+ actor: string;
18
+ details: Record<string, unknown>;
19
+ changeId?: string;
20
+ }
21
+ /**
22
+ * Python の `json.dumps(x, ensure_ascii=False)` と同じ文字列(区切りは `, ` と `: `)。
23
+ * 送る内容(--payload)と、RECORD_DRY_RUN の表示を、現行と一字一句同じにするため
24
+ */
25
+ export declare function pythonJsonDumps(value: unknown): string;
26
+ /**
27
+ * コミット(`git rev-parse --short HEAD`。取れなければ unknown)。dirtyPaths に未コミットの変更があれば `-dirty`
28
+ * (`git diff-index --quiet HEAD -- <paths>` が 0 以外。git が無い・失敗も -dirty。現行と同じ)
29
+ */
30
+ export declare function commitOf(repo: string | undefined, dirtyPaths: string[], run?: RunCommand): string;
31
+ /** 実行者(`$(whoami)@$(hostname -s)`) */
32
+ export declare function actorOf(): string;
33
+ export interface SendOptions {
34
+ functionName: string;
35
+ region: string;
36
+ dryRun: boolean;
37
+ /** 警告の出力先(標準エラー。バイト列も受ける) */
38
+ warn: (message: string | Buffer) => void;
39
+ run?: RunCommand;
40
+ }
41
+ /** 記録 Lambda を呼ぶ。失敗は警告だけ(例外にしない)。記録 Lambda が無い環境は黙ってスキップ */
42
+ export declare function sendSystemChange(payload: SystemChangePayload, options: SendOptions): void;
43
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/send.ts"],"names":[],"mappings":"AAUA,2BAA2B;AAC3B,MAAM,MAAM,UAAU,GAAG,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACvB;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAE9E,eAAO,MAAM,UAAU,EAAE,UAQxB,CAAC;AAEF,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOtD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,GAAG,aAAa,GAAG,MAAM,CAOjG;AAED,sCAAsC;AACtC,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACzC,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAYD,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAsCzF"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 記録 Lambda(`<app>-<env>-system-change-recorder`)への送信(両アプリの terraform-wrapper.sh の record_change を
3
+ * 移植したもの)。
4
+ *
5
+ * 経路は現行のまま: AWS CLI の `aws lambda invoke`(認証は AWS CLI の認証情報。`AWS_PROFILE` などは環境変数で
6
+ * 引き継ぐ)。記録の失敗は、警告を出すだけで、例外にしない(apply・destroy を止めない)。
7
+ */
8
+ import { spawnSync } from 'node:child_process';
9
+ import { hostname, userInfo } from 'node:os';
10
+ export const runCommand = (command, args, options) => {
11
+ const r = spawnSync(command, args, { cwd: options?.cwd, stdio: ['ignore', 'pipe', 'pipe'] });
12
+ return {
13
+ status: r.status,
14
+ stdout: r.stdout ?? Buffer.alloc(0),
15
+ stderr: r.stderr ?? Buffer.alloc(0),
16
+ error: r.error,
17
+ };
18
+ };
19
+ /**
20
+ * Python の `json.dumps(x, ensure_ascii=False)` と同じ文字列(区切りは `, ` と `: `)。
21
+ * 送る内容(--payload)と、RECORD_DRY_RUN の表示を、現行と一字一句同じにするため
22
+ */
23
+ export function pythonJsonDumps(value) {
24
+ if (Array.isArray(value))
25
+ return `[${value.map(pythonJsonDumps).join(', ')}]`;
26
+ if (value !== null && typeof value === 'object') {
27
+ const entries = Object.entries(value).filter(([, v]) => v !== undefined);
28
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}: ${pythonJsonDumps(v)}`).join(', ')}}`;
29
+ }
30
+ return JSON.stringify(value);
31
+ }
32
+ /**
33
+ * コミット(`git rev-parse --short HEAD`。取れなければ unknown)。dirtyPaths に未コミットの変更があれば `-dirty`
34
+ * (`git diff-index --quiet HEAD -- <paths>` が 0 以外。git が無い・失敗も -dirty。現行と同じ)
35
+ */
36
+ export function commitOf(repo, dirtyPaths, run = runCommand) {
37
+ const head = run('git', ['rev-parse', '--short', 'HEAD'], { cwd: repo });
38
+ let commit = head.status === 0 && !head.error ? head.stdout.toString('utf8').replace(/\n+$/, '') : 'unknown';
39
+ const diff = run('git', ['diff-index', '--quiet', 'HEAD', '--', ...dirtyPaths], { cwd: repo });
40
+ if (diff.status !== 0 || diff.error)
41
+ commit = `${commit}-dirty`;
42
+ return commit;
43
+ }
44
+ /** 実行者(`$(whoami)@$(hostname -s)`) */
45
+ export function actorOf() {
46
+ return `${userInfo().username}@${hostname().split('.')[0]}`;
47
+ }
48
+ const PREFIX = '[change-record]';
49
+ const NL = Buffer.from('\n');
50
+ /** 末尾の改行(0x0a)を除く(bash の `$(...)` と同じ) */
51
+ function trimTrailingNewlines(buf) {
52
+ let end = buf.length;
53
+ while (end > 0 && buf[end - 1] === 0x0a)
54
+ end -= 1;
55
+ return buf.subarray(0, end);
56
+ }
57
+ /** 記録 Lambda を呼ぶ。失敗は警告だけ(例外にしない)。記録 Lambda が無い環境は黙ってスキップ */
58
+ export function sendSystemChange(payload, options) {
59
+ const body = pythonJsonDumps(payload);
60
+ if (options.dryRun) {
61
+ options.warn(`${PREFIX}[dry-run] ${options.functionName} <- ${body}\n`);
62
+ return;
63
+ }
64
+ const run = options.run ?? runCommand;
65
+ const r = run('aws', [
66
+ 'lambda',
67
+ 'invoke',
68
+ '--function-name',
69
+ options.functionName,
70
+ '--region',
71
+ options.region,
72
+ '--cli-binary-format',
73
+ 'raw-in-base64-out',
74
+ '--cli-connect-timeout',
75
+ '5',
76
+ '--cli-read-timeout',
77
+ '30',
78
+ '--payload',
79
+ body,
80
+ '/dev/null',
81
+ ]);
82
+ // 現行は `out="$(… 2>&1)"`(標準出力と標準エラーをまとめ、末尾の改行を除く)
83
+ const errText = r.error ? Buffer.from(`${r.error.message}\n`) : Buffer.alloc(0);
84
+ const out = trimTrailingNewlines(Buffer.concat([r.stdout, r.stderr, errText]));
85
+ const text = out.toString('utf8');
86
+ if (r.status !== 0 || r.error) {
87
+ if (text.includes('ResourceNotFoundException'))
88
+ return; // 記録 Lambda が無い環境は、スキップ
89
+ // 現行は `$(echo "$out" | head -c 200)`(バイトで 200。末尾の改行は $(...) が除く)
90
+ const head = trimTrailingNewlines(Buffer.concat([out, NL]).subarray(0, 200));
91
+ options.warn(Buffer.concat([Buffer.from(`${PREFIX} 記録に失敗しました(処理は続行): `), head, NL]));
92
+ }
93
+ else if (text.includes('"FunctionError"')) {
94
+ options.warn(`${PREFIX} 記録 Lambda がエラーを返しました(処理は続行)\n`);
95
+ }
96
+ }
97
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/send.ts"],"names":[],"mappings":"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"}
package/docs/NOTIFY.md CHANGED
@@ -215,6 +215,6 @@ export const handler = createAlertsCleanupHandler();
215
215
 
216
216
  ### 管理画面(各アプリ)
217
217
 
218
- - 一覧・詳細はアプリが書く。`<Datagrid bulkActionButtons={false}>`、Edit なしの読み取り専用にする。ただし読み取り専用は UI だけの担保(Records 本体はリソースごとの認可を持たない)。管理者以外から `alerts` を読めないことは、各アプリの権限で確認する
218
+ - 一覧・詳細は、本ライブラリの `AlertsList` / `AlertsShow`(`@exabugs/dynamodb-client/integrations/react-admin`。1.19.0。使い方は [react-admin-integration.md](react-admin-integration.md#運用の画面alertssystemchanges))を使う。読み取り専用(作成・編集・一括操作・書き出しなし)。ただし読み取り専用は UI だけの担保(Records 本体はリソースごとの認可を持たない)。管理者以外から `alerts` を読めないことは、各アプリの権限で確認する
219
219
  - URL は `<adminBaseUrl>/alerts/<id>/show`(BrowserRouter)。未ログインで開くと、ログイン後にダッシュボードへ飛んで遷移先が消えるので、各アプリで元の URL に戻す処理が要る
220
220
  - 詳細に `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)
@@ -100,12 +100,81 @@ export const handler = createSystemChangeHandler({
100
100
  - `systemChangesCollectionOf(raw)`: Records のコレクションを書き込み先にする
101
101
  - `parseSystemChangeInput` / `toSystemChangeRecord` / `decideRunWrite`: 純粋な関数
102
102
  - `formatSystemChangeMessage(app, env, record, id, adminBaseUrl?)` / `formatDuration` / `lambdaReleaseNames` / `shouldNotify`
103
+ - 表示の部品(`labels.ts`。Slack の文言と管理画面が使う): `SYSTEM_CHANGE_KIND_LABELS` / `SYSTEM_CHANGE_RESULT_LABELS`(日本語のラベル)、`parseLambdaReleases(value)`(`details.lambdaReleases` の形の合う要素だけ。`lambdaReleaseNames` もこれを使う)
104
+ - 管理画面の一覧・詳細は `SystemChangesList` / `SystemChangesShow`(`@exabugs/dynamodb-client/integrations/react-admin`。1.19.0。[react-admin-integration.md](react-admin-integration.md#運用の画面alertssystemchanges))
103
105
  - 投稿は notify の `postSlackText`(`createSlackChannel` と同じ送信。URL の接頭辞の確認、URL を出さないエラー)
104
106
 
107
+ ## 記録の CLI(`dynamodb-system-change`。1.20.0)
108
+
109
+ terraform の apply・destroy の開始と終了を、記録 Lambda(`<app>-<env>-system-change-recorder`)に送る CLI([ADR 0006](adr/0006-library-owns-table-operations.md) の段階 5 = (c))。asanowa・astro の `terraform-wrapper.sh` の「記録に送る部分」(開始・終了の呼び出し、`details` の組み立て、`parse-lambda-releases.py`)を Node にした。**bash のラッパーは各アプリに残し**(state のバックエンド、plan の保存と確認、環境の検査はアプリごとに違う)、記録だけをこの CLI に委ねる。
110
+
111
+ - 経路は従来のまま: AWS CLI の `aws lambda invoke`(`--cli-connect-timeout 5 --cli-read-timeout 30`。認証は AWS CLI の認証情報で、`AWS_PROFILE` などは環境変数で引き継ぐ)。送る本文は従来の Python の `json.dumps` と同じ文字列
112
+ - **記録の失敗で apply を止めない**: `start` / `finish` は、記録の失敗・引数の誤りでも終了コード 0(警告は標準エラーに `[change-record] …`)。記録 Lambda が無い環境(`ResourceNotFoundException`)は黙ってスキップ。サブコマンドの誤りだけ 2
113
+ - 受け渡し: `start` は変更 ID(ULID)を標準出力に 1 行で出す(記録の成否によらず。`SKIP_CHANGE_RECORD=1` では何も出さない)。ラッパーはシェルの変数に持ち、`finish --change-id` に渡す
114
+ - 環境変数は従来と同じ: `RECORD_DRY_RUN=1`(呼ばずに、送る内容を表示)、`SKIP_CHANGE_RECORD=1`(記録しない)、`AWS_REGION`(無ければ `ap-northeast-1`)
115
+
116
+ ```text
117
+ dynamodb-system-change start --app <app> --env <env> --command <cmd> [共通]
118
+ dynamodb-system-change finish --app <app> --env <env> --command <cmd> --exit-code <n> [--change-id <id>] [--log <file>] [共通]
119
+ 共通: [--repo <dir>] [--function <name>] [--target <target>] [--region <region>]
120
+ [--releases-max-items <n|none>] [--releases-max-bytes <n|none>] [--dirty-path <path>...]
121
+ ```
122
+
123
+ | 引数 | 内容 |
124
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
125
+ | `--app` / `--env` | `details.app` / `details.env`。既定の関数名 `<app>-<env>-system-change-recorder` と対象 `terraform:<app>` にも使う |
126
+ | `--command` | `details.command`(`apply` / `apply-auto` / `destroy` など)。`destroy` では Lambda のリリースを取り出さない |
127
+ | `--exit-code` | terraform の終了コード。0 は `succeeded`、それ以外は `failed` と `details.exit_code` |
128
+ | `--log` | plan / apply のログ。成功なら最後の完了の行から `add` / `change` / `destroy`、Lambda のリリースから `lambdaReleases` |
129
+ | `--repo` | git を実行するディレクトリ(既定: カレント)。`commit` は `git rev-parse --short HEAD`(取れなければ `unknown`) |
130
+ | `--dirty-path` | 未コミットの変更を調べる場所(`git diff-index --quiet HEAD -- <path>...`)。あれば `commit` に `-dirty`。最後に書く |
131
+ | `--releases-max-items` | `lambdaReleases` の件数の上限(既定 `none`) |
132
+ | `--releases-max-bytes` | `lambdaReleases` の JSON のバイト数の上限(既定 3000。details の上限 4096 に収める)。失敗を先に残し、省いた件数は `lambdaReleasesOmitted` |
133
+
134
+ `actor` は `<ユーザー名>@<ホスト名の先頭>`(従来の `$(whoami)@$(hostname -s)`)。
135
+
136
+ ### ラッパーからの呼び方
137
+
138
+ `set -euo pipefail` のラッパーでも止まらないよう、呼び出しには `|| true` を付ける(CLI が入っていない・node が無いときも apply を進める)。CLI が無いときは、記録が欠けたことに気づけるよう警告を出す。記録の部分は次のとおり(`$CHANGE_LOG` と `trap` の扱い、plan の確認はアプリのまま。`<…>` はアプリごとの値):
139
+
140
+ ```bash
141
+ SYSTEM_CHANGE="<node_modules の .bin>/dynamodb-system-change" # npx は標準出力に余計な行を出しうるので使わない
142
+ CHANGE_ARGS=(--app "<アプリ名>" --env "$ENV" --command "$COMMAND" [--repo "<git を実行する場所>"] --dirty-path <調べる場所>...)
143
+
144
+ begin_change() {
145
+ [ -x "$SYSTEM_CHANGE" ] || echo "[change-record] CLI がありません(記録をスキップ)" >&2
146
+ CHANGE_ID="$("$SYSTEM_CHANGE" start "${CHANGE_ARGS[@]}" || true)"
147
+ CHANGE_OPEN=1
148
+ }
149
+
150
+ finish_change() {
151
+ local rc=$?
152
+ trap - EXIT
153
+ if [ "$CHANGE_OPEN" = "1" ]; then
154
+ CHANGE_OPEN=0
155
+ "$SYSTEM_CHANGE" finish "${CHANGE_ARGS[@]}" --change-id "$CHANGE_ID" --exit-code "$rc" --log "$CHANGE_LOG" || true
156
+ fi
157
+ rm -f "$CHANGE_LOG"
158
+ return "$rc"
159
+ }
160
+ ```
161
+
162
+ アプリごとの `CHANGE_ARGS`(従来の記録と同じ commit・details になる。`lambdaReleases` の上限は、両アプリとも既定の 3000 バイトを使う):
163
+
164
+ - **asanowa**: `--app "$APP" --env "$ENV" --command "$COMMAND" --dirty-path "$INFRA_ROOT" "$INFRA_ROOT/../functions" "$INFRA_ROOT/../packages"`(`--repo` は不要。ラッパーが `cd` した先で git を実行する)
165
+ - **astro**: `--app "$PROJECT" --env "$ENV" --command "$COMMAND" --repo "$REPO_ROOT" --dirty-path infra apps functions packages`。上限は asanowa の既定(3000 バイト)に揃える(従来の「30 件まで」はやめる。件数の上限は付けない。64 文字の名前が並ぶと 30 件 → 約 27 件になり、超えた分は `lambdaReleasesOmitted` に残る)
166
+
167
+ `--releases-max-items` / `--releases-max-bytes` は、既定と違う上限が要るときだけ使う。
168
+
169
+ 置き換えた後は、`record_change`・`new_change_id`・`finish_change` の Python と `parse-lambda-releases.py` を消せる。両アプリの `test-terraform-wrapper.sh` は、偽の `aws` を PATH に置く方式なので、そのまま使える(1.20.0 の開発時に、両アプリの検査がこの形のラッパーで全て通ることを確かめた)。
170
+
171
+ 部品(`@exabugs/dynamodb-client/system-changes/cli`): `extractLambdaReleases(logText)`(`parse-lambda-releases.py` の移植。出力は同じ)、`buildFinishDetails` / `limitReleases` / `resourceCountsOf`、`main(argv, io, deps?)`。
172
+
105
173
  ## テスト
106
174
 
107
175
  - 単体: `src/system-changes/__tests__/`。ハンドラは、本物の `DynamoClient` に `fetch` だけを差し替えて固定する(`connect()` の呼び忘れを見逃さない)
108
176
  - 統合(DynamoDB Local。CI には入っていない): `src/system-changes/__tests__/system-changes.integration.test.ts`(実行方法は PUBLISHING.md)
177
+ - CLI: `__tests__/tools/dynamodb-system-change-*.test.ts`。Lambda のリリースの取り出しは、両アプリの本物のログ(`__tests__/tools/testdata/terraform-logs`。アカウント ID・URL・Cognito のユーザープール ID・ドメインは伏せた)で Python 版と同じ出力を固定する。送り方は偽の `aws` / `git`、止めないことは `set -euo pipefail` の bash のラッパーから CLI をプロセスとして呼んで確かめる
109
178
 
110
179
  ## アプリの移行(公開後の別作業)
111
180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exabugs/dynamodb-client",
3
- "version": "1.18.0",
3
+ "version": "1.20.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",
@@ -8,7 +8,8 @@
8
8
  "main": "./dist/index.js",
9
9
  "types": "./dist/index.d.ts",
10
10
  "bin": {
11
- "dynamodb-access-check": "./bin/dynamodb-access-check.js"
11
+ "dynamodb-access-check": "./bin/dynamodb-access-check.js",
12
+ "dynamodb-system-change": "./bin/dynamodb-system-change.js"
12
13
  },
13
14
  "keywords": [
14
15
  "dynamodb",
@@ -129,12 +130,20 @@
129
130
  "./system-changes": {
130
131
  "import": "./dist/system-changes/index.js",
131
132
  "types": "./dist/system-changes/index.d.ts"
133
+ },
134
+ "./system-changes/cli": {
135
+ "import": "./dist/tools/dynamodb-system-change/index.js",
136
+ "types": "./dist/tools/dynamodb-system-change/index.d.ts"
132
137
  }
133
138
  },
134
139
  "peerDependencies": {
140
+ "@mui/material": "^5.16.12 || ^6.0.0 || ^7.0.0",
135
141
  "react-admin": "^5.0.0"
136
142
  },
137
143
  "peerDependenciesMeta": {
144
+ "@mui/material": {
145
+ "optional": true
146
+ },
138
147
  "react-admin": {
139
148
  "optional": true
140
149
  }
@@ -180,6 +189,9 @@
180
189
  "@eslint/js": "^9.0.0",
181
190
  "@faker-js/faker": "^10.2.0",
182
191
  "@redocly/cli": "^2.16.0",
192
+ "@testing-library/dom": "^10.4.2",
193
+ "@testing-library/jest-dom": "^6.9.1",
194
+ "@testing-library/react": "^16.3.3",
183
195
  "@trivago/prettier-plugin-sort-imports": "^4.3.0",
184
196
  "@types/aws-lambda": "^8.10.0",
185
197
  "@types/ngeohash": "^0.6.8",
@@ -190,6 +202,7 @@
190
202
  "esbuild": "^0.27.1",
191
203
  "eslint": "^9.0.0",
192
204
  "globals": "^15.0.0",
205
+ "jsdom": "^26.1.0",
193
206
  "orval": "^8.2.0",
194
207
  "prettier": "^3.0.0",
195
208
  "react-admin": "^5.0.0",