@exabugs/dynamodb-client 1.19.0 → 1.21.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 (52) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/bin/dynamodb-system-change.js +6 -0
  4. package/dist/server/handler.cjs +3 -3
  5. package/dist/server/media-handler.cjs +2 -2
  6. package/dist/server/media-process-handler-dlq.cjs +2 -2
  7. package/dist/server/media-process-handler.cjs +2 -2
  8. package/dist/tools/dynamodb-system-change/cli.d.ts +35 -0
  9. package/dist/tools/dynamodb-system-change/cli.d.ts.map +1 -0
  10. package/dist/tools/dynamodb-system-change/cli.js +226 -0
  11. package/dist/tools/dynamodb-system-change/cli.js.map +1 -0
  12. package/dist/tools/dynamodb-system-change/details.d.ts +42 -0
  13. package/dist/tools/dynamodb-system-change/details.d.ts.map +1 -0
  14. package/dist/tools/dynamodb-system-change/details.js +59 -0
  15. package/dist/tools/dynamodb-system-change/details.js.map +1 -0
  16. package/dist/tools/dynamodb-system-change/index.d.ts +13 -0
  17. package/dist/tools/dynamodb-system-change/index.d.ts.map +1 -0
  18. package/dist/tools/dynamodb-system-change/index.js +9 -0
  19. package/dist/tools/dynamodb-system-change/index.js.map +1 -0
  20. package/dist/tools/dynamodb-system-change/releases.d.ts +34 -0
  21. package/dist/tools/dynamodb-system-change/releases.d.ts.map +1 -0
  22. package/dist/tools/dynamodb-system-change/releases.js +108 -0
  23. package/dist/tools/dynamodb-system-change/releases.js.map +1 -0
  24. package/dist/tools/dynamodb-system-change/run.d.ts +2 -0
  25. package/dist/tools/dynamodb-system-change/run.d.ts.map +1 -0
  26. package/dist/tools/dynamodb-system-change/run.js +6 -0
  27. package/dist/tools/dynamodb-system-change/run.js.map +1 -0
  28. package/dist/tools/dynamodb-system-change/send.d.ts +43 -0
  29. package/dist/tools/dynamodb-system-change/send.d.ts.map +1 -0
  30. package/dist/tools/dynamodb-system-change/send.js +97 -0
  31. package/dist/tools/dynamodb-system-change/send.js.map +1 -0
  32. package/docs/DYNAMODB_ACCESS.md +1 -1
  33. package/docs/NOTIFY.md +2 -2
  34. package/docs/SYSTEM_CHANGES.md +68 -1
  35. package/package.json +7 -2
  36. package/terraform/README.md +9 -0
  37. package/terraform/alerts-cleanup/README.md +65 -0
  38. package/terraform/alerts-cleanup/main.tf +111 -0
  39. package/terraform/alerts-cleanup/outputs.tf +14 -0
  40. package/terraform/alerts-cleanup/variables.tf +42 -0
  41. package/terraform/alerts-cleanup/versions.tf +16 -0
  42. package/terraform/ops-notify/README.md +109 -0
  43. package/terraform/ops-notify/main.tf +295 -0
  44. package/terraform/ops-notify/outputs.tf +19 -0
  45. package/terraform/ops-notify/us-east-1.tf +68 -0
  46. package/terraform/ops-notify/variables.tf +105 -0
  47. package/terraform/ops-notify/versions.tf +18 -0
  48. package/terraform/system-change-recorder/README.md +71 -0
  49. package/terraform/system-change-recorder/main.tf +122 -0
  50. package/terraform/system-change-recorder/outputs.tf +14 -0
  51. package/terraform/system-change-recorder/variables.tf +53 -0
  52. package/terraform/system-change-recorder/versions.tf +16 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.21.0] - 2026-10-24
11
+
12
+ ### Added
13
+
14
+ - **Terraform モジュール `terraform/ops-notify`・`terraform/alerts-cleanup`・`terraform/system-change-recorder`**(ADR 0006 の段階 6 = (b)): asanowa・astro に複製されていた運用の Lambda の Terraform(通知、alerts の定時削除、システム変更履歴の記録)を 1 つにした。`node_modules` のパスで `source` を指定して使う(`terraform/media` と同じ流儀)。**Lambda の成果物(エントリ)はアプリが作り、`source_file` と `handler` で渡す**(ライブラリの dist は使わない。`exports` への追加もない)。リソースのアドレスと AWS 上の名前は両アプリの旧モジュールと同じなので、module ブロックの名前を変えずに `source` だけを差し替えれば、`moved` なしで置き換えが起きない(astro dev の実機の plan で確認: 0 to add / 0 to destroy、in-place の 4 件だけ)。文言は asanowa を既定にした。ops-notify は asanowa の us-east-1 のアラームの転送(`forward_us_east_1_alarms`、既定 false)も持つので、**呼び出し側は `providers = { aws.us_east_1 = aws.us_east_1 }` を常に渡す**。provider は `hashicorp/aws >= 5.30, < 7.0`。使い方は各ディレクトリの README
15
+ - DynamoDB の直接アクセスの許可リストに、`terraform/ops-notify` の通知 Lambda のロール(別テーブル `notices` への `UpdateItem`。`src/notify/store.ts` と同じ理由の permanent)を加えた
16
+
17
+ ### Fixed
18
+
19
+ - `npm pack` が `terraform/` の中の Terraform の作業ファイル(`.terraform/`〔プロバイダのバイナリ〕、`.terraform.lock.hcl`、state、plan)を拾わないようにした(`terraform/.npmignore`。`files` の `terraform` の中で、`terraform init` などをしたときに入っていた)
20
+
21
+ ## [1.20.0] - 2026-10-24
22
+
23
+ ### Added
24
+
25
+ - **CLI `dynamodb-system-change start|finish`**(ADR 0006 の段階 5 = (c)): terraform の apply・destroy の開始と終了を、記録 Lambda(`<app>-<env>-system-change-recorder`)に送る。asanowa・astro の `terraform-wrapper.sh` の記録の部分(開始・終了の呼び出し、`details` の組み立て、`parse-lambda-releases.py`)を Node にした。bash のラッパーは各アプリに残り、記録だけを委ねる。経路は従来と同じ AWS CLI の `aws lambda invoke`、送る本文も同じ。**記録の失敗・引数の誤りでも終了コード 0**(apply を止めない)。start は変更 ID(ULID)を標準出力に出し、ラッパーが finish の `--change-id` に渡す。両アプリの差(アプリ名、git の場所と未コミットの変更を調べる場所、`lambdaReleases` の切り詰め)は引数で渡す。使い方は `docs/SYSTEM_CHANGES.md` の「記録の CLI」
26
+ - `@exabugs/dynamodb-client/system-changes/cli`: `extractLambdaReleases`(`parse-lambda-releases.py` の移植。出力は同じ)、`buildFinishDetails` / `limitReleases` / `resourceCountsOf`、`main` / `parseRecordArgs`、型
27
+
10
28
  ## [1.19.0] - 2026-10-24
11
29
 
12
30
  ### Added
package/README.md CHANGED
@@ -198,7 +198,7 @@ const article = await articles.findOne({ title: 'Hello DynamoDB' });
198
198
  - **[疎な検索の警告と対応](docs/SCAN_WARNING.md)** - 検索を打ち切らず、警告(SPARSE_SCAN)で気づいてインデックス・スキーマ・ロジックを見直す
199
199
  - **[Records クライアントのヘルパー](docs/RECORDS_CLIENT.md)** - Lambda から Records を呼ぶ定型(`createRecordsClient` / `withCollection` / `lazyCollection`)
200
200
  - **[運用通知](docs/NOTIFY.md)** - ログ・アラームを、Slack・メールで管理者へ通知する部品(`@exabugs/dynamodb-client/notify`)
201
- - **[システム変更履歴](docs/SYSTEM_CHANGES.md)** - インフラの変更・リリースを `systemChanges` に記録し、Slack に送る部品と記録 Lambda のハンドラ(`@exabugs/dynamodb-client/system-changes`)
201
+ - **[システム変更履歴](docs/SYSTEM_CHANGES.md)** - インフラの変更・リリースを `systemChanges` に記録し、Slack に送る部品と記録 Lambda のハンドラ(`@exabugs/dynamodb-client/system-changes`)、terraform のラッパーから記録する CLI(`dynamodb-system-change`)
202
202
  - **[Deployment](docs/DEPLOYMENT.md)** - Production deployment guide
203
203
  - **[Terraform Modules](terraform/README.md)** - Infrastructure as Code
204
204
 
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * terraform の apply・destroy のシステム変更履歴への記録(docs/SYSTEM_CHANGES.md の「記録の CLI」)
5
+ */
6
+ import '../dist/tools/dynamodb-system-change/run.js';
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.19.0
2
- // Built: 2026-10-24T07:44:43.731Z
1
+ // @exabugs/dynamodb-client v1.21.0
2
+ // Built: 2026-10-24T14:04:54.676Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -38980,7 +38980,7 @@ async function handler(event) {
38980
38980
  return createCorsResponse(HTTP_STATUS.OK);
38981
38981
  }
38982
38982
  if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
38983
- const version = "1.19.0";
38983
+ const version = "1.21.0";
38984
38984
  return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
38985
38985
  }
38986
38986
  if (event.requestContext.http.method !== "POST") {
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.19.0
2
- // Built: 2026-10-24T07:44:43.731Z
1
+ // @exabugs/dynamodb-client v1.21.0
2
+ // Built: 2026-10-24T14:04:54.676Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.19.0
2
- // Built: 2026-10-24T07:44:43.731Z
1
+ // @exabugs/dynamodb-client v1.21.0
2
+ // Built: 2026-10-24T14:04:54.676Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.19.0
2
- // Built: 2026-10-24T07:44:43.731Z
1
+ // @exabugs/dynamodb-client v1.21.0
2
+ // Built: 2026-10-24T14:04:54.676Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -0,0 +1,35 @@
1
+ import { type ReleasesLimit } from './details.js';
2
+ import { type RunCommand } from './send.js';
3
+ export interface CliIo {
4
+ out: (s: string) => void;
5
+ /** 標準エラー(改行は呼び出し側が付ける。バイト列も受ける) */
6
+ err: (s: string | Buffer) => void;
7
+ }
8
+ export interface CliDeps {
9
+ env?: NodeJS.ProcessEnv;
10
+ run?: RunCommand;
11
+ newChangeId?: () => string;
12
+ actor?: () => string;
13
+ }
14
+ export declare const USAGE = "\u4F7F\u3044\u65B9:\n dynamodb-system-change start --app <app> --env <env> --command <cmd> [\u5171\u901A]\n \u2192 \u5909\u66F4 ID\uFF08ULID\uFF09\u3092\u6A19\u6E96\u51FA\u529B\u306B\u51FA\u3059\uFF08SKIP_CHANGE_RECORD=1 \u3067\u306F\u4F55\u3082\u51FA\u3055\u306A\u3044\uFF09\n dynamodb-system-change finish --app <app> --env <env> --command <cmd> --exit-code <n>\n [--change-id <id>] [--log <file>] [\u5171\u901A]\n\n --app <app> \u30A2\u30D7\u30EA\u540D\uFF08details.app\u3002\u65E2\u5B9A\u306E\u95A2\u6570\u540D\u30FB\u5BFE\u8C61\u306B\u4F7F\u3046\uFF09\n --env <env> \u74B0\u5883\uFF08details.env\uFF09\n --command <cmd> \u30E9\u30C3\u30D1\u30FC\u306E\u30B3\u30DE\u30F3\u30C9\uFF08details.command\u3002destroy \u3067\u306F\u30EA\u30EA\u30FC\u30B9\u3092\u53D6\u308A\u51FA\u3055\u306A\u3044\uFF09\n --exit-code <n> terraform \u306E\u7D42\u4E86\u30B3\u30FC\u30C9\uFF080 = succeeded\u3001\u305D\u308C\u4EE5\u5916 = failed\u30020 \u4EE5\u5916\u306F details.exit_code\uFF09\n --change-id <id> start \u304C\u51FA\u3057\u305F\u5909\u66F4 ID\uFF08\u7A7A\u306A\u3089 changeId \u306A\u3057\u3067\u8A18\u9332\uFF09\n --log <file> plan / apply \u306E\u30ED\u30B0\uFF08\u4EF6\u6570\u3068 Lambda \u306E\u30EA\u30EA\u30FC\u30B9\u3092\u53D6\u308A\u51FA\u3059\uFF09\n\u5171\u901A:\n --repo <dir> git \u3092\u5B9F\u884C\u3059\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\uFF08\u65E2\u5B9A: \u30AB\u30EC\u30F3\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\uFF09\n --dirty-path <path>... \u672A\u30B3\u30DF\u30C3\u30C8\u306E\u5909\u66F4\u3092\u8ABF\u3079\u308B\u5834\u6240\uFF08\u3042\u308C\u3070 commit \u306B -dirty\u3002\u65E2\u5B9A: \u4F5C\u696D\u30C4\u30EA\u30FC\u5168\u4F53\uFF09\n --function <name> \u8A18\u9332 Lambda\uFF08\u65E2\u5B9A: <app>-<env>-system-change-recorder\uFF09\n --target <target> \u5BFE\u8C61\uFF08\u65E2\u5B9A: terraform:<app>\uFF09\n --region <region> \u65E2\u5B9A: AWS_REGION\u3001\u7121\u3051\u308C\u3070 ap-northeast-1\n --releases-max-items <n|none> lambdaReleases \u306E\u4EF6\u6570\u306E\u4E0A\u9650\uFF08\u65E2\u5B9A: none\uFF09\n --releases-max-bytes <n|none> lambdaReleases \u306E JSON \u306E\u30D0\u30A4\u30C8\u6570\u306E\u4E0A\u9650\uFF08\u65E2\u5B9A: 3000\uFF09\n\n\u74B0\u5883\u5909\u6570: RECORD_DRY_RUN=1\uFF08\u547C\u3073\u51FA\u3055\u305A\u3001\u9001\u308B\u5185\u5BB9\u3092\u6A19\u6E96\u30A8\u30E9\u30FC\u306B\u51FA\u3059\uFF09\u3001SKIP_CHANGE_RECORD=1\uFF08\u8A18\u9332\u3057\u306A\u3044\uFF09\n\u7D42\u4E86\u30B3\u30FC\u30C9: start / finish \u306F\u5E38\u306B 0\uFF08\u8A18\u9332\u306E\u5931\u6557\u3067 apply \u3092\u6B62\u3081\u306A\u3044\uFF09\u3002--help \u306F 0\u3001\u30B5\u30D6\u30B3\u30DE\u30F3\u30C9\u306E\u8AA4\u308A\u306F 2";
15
+ export interface RecordArgs {
16
+ app: string;
17
+ env: string;
18
+ command: string;
19
+ repo?: string;
20
+ dirtyPaths: string[];
21
+ functionName: string;
22
+ target: string;
23
+ region?: string;
24
+ limit: ReleasesLimit;
25
+ exitCode?: number;
26
+ changeId?: string;
27
+ log?: string;
28
+ }
29
+ /** 引数を解釈する(誤りは Error)。`--change-id` と `--log` は空の値を許す(ラッパーの変数が空のとき) */
30
+ export declare function parseRecordArgs(sub: 'start' | 'finish', argv: string[]): RecordArgs;
31
+ /** 終了の details(件数・exit_code・lambdaReleases)。destroy ではリリースを取り出さない */
32
+ export declare function finishDetailsOf(args: RecordArgs): Record<string, unknown>;
33
+ /** CLI 本体。終了コードを返す(process.exitCode は run.ts が設定する) */
34
+ export declare function main(argv: string[], io: CliIo, deps?: CliDeps): number;
35
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/cli.ts"],"names":[],"mappings":"AAcA,OAAO,EAA8B,KAAK,aAAa,EAAsB,MAAM,cAAc,CAAC;AAElG,OAAO,EACL,KAAK,UAAU,EAKhB,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,mCAAmC;IACnC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,KAAK,wxFAsBsD,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAyBD,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAsDnF;AAYD,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzE;AA8DD,uDAAuD;AACvD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAE,OAAY,GAAG,MAAM,CAW1E"}
@@ -0,0 +1,226 @@
1
+ /**
2
+ * `dynamodb-system-change start|finish`: terraform の apply・destroy の開始と終了を、システム変更履歴に記録する
3
+ * (ADR 0006 の段階 5 = (c)。docs/SYSTEM_CHANGES.md の「記録の CLI」)。
4
+ *
5
+ * 各アプリの terraform-wrapper.sh(bash)は残し、記録に送る部分(開始・終了の呼び出し、details の組み立て、
6
+ * Lambda のリリースの取り出し)だけをこの CLI に委ねる。**記録の失敗で apply を止めない**: start / finish は、
7
+ * 引数の誤りを含めて、常に終了コード 0 で終わる(警告は標準エラー)。
8
+ *
9
+ * 受け渡し: start は変更 ID(ULID)を標準出力に 1 行で出す(SKIP_CHANGE_RECORD=1 では何も出さない)。
10
+ * ラッパーはそれをシェルの変数に持ち、finish の `--change-id` に渡す(現行の CHANGE_ID と同じ)。
11
+ */
12
+ import { readFileSync } from 'node:fs';
13
+ import { ulid } from 'ulid';
14
+ import { DEFAULT_RELEASES_MAX_BYTES, buildFinishDetails } from './details.js';
15
+ import { extractLambdaReleases } from './releases.js';
16
+ import { actorOf, commitOf, sendSystemChange, } from './send.js';
17
+ export const USAGE = `使い方:
18
+ dynamodb-system-change start --app <app> --env <env> --command <cmd> [共通]
19
+ → 変更 ID(ULID)を標準出力に出す(SKIP_CHANGE_RECORD=1 では何も出さない)
20
+ dynamodb-system-change finish --app <app> --env <env> --command <cmd> --exit-code <n>
21
+ [--change-id <id>] [--log <file>] [共通]
22
+
23
+ --app <app> アプリ名(details.app。既定の関数名・対象に使う)
24
+ --env <env> 環境(details.env)
25
+ --command <cmd> ラッパーのコマンド(details.command。destroy ではリリースを取り出さない)
26
+ --exit-code <n> terraform の終了コード(0 = succeeded、それ以外 = failed。0 以外は details.exit_code)
27
+ --change-id <id> start が出した変更 ID(空なら changeId なしで記録)
28
+ --log <file> plan / apply のログ(件数と Lambda のリリースを取り出す)
29
+ 共通:
30
+ --repo <dir> git を実行するディレクトリ(既定: カレントディレクトリ)
31
+ --dirty-path <path>... 未コミットの変更を調べる場所(あれば commit に -dirty。既定: 作業ツリー全体)
32
+ --function <name> 記録 Lambda(既定: <app>-<env>-system-change-recorder)
33
+ --target <target> 対象(既定: terraform:<app>)
34
+ --region <region> 既定: AWS_REGION、無ければ ap-northeast-1
35
+ --releases-max-items <n|none> lambdaReleases の件数の上限(既定: none)
36
+ --releases-max-bytes <n|none> lambdaReleases の JSON のバイト数の上限(既定: ${DEFAULT_RELEASES_MAX_BYTES})
37
+
38
+ 環境変数: RECORD_DRY_RUN=1(呼び出さず、送る内容を標準エラーに出す)、SKIP_CHANGE_RECORD=1(記録しない)
39
+ 終了コード: start / finish は常に 0(記録の失敗で apply を止めない)。--help は 0、サブコマンドの誤りは 2`;
40
+ const SINGLE = {
41
+ '--app': 'app',
42
+ '--env': 'env',
43
+ '--command': 'command',
44
+ '--repo': 'repo',
45
+ '--function': 'functionName',
46
+ '--target': 'target',
47
+ '--region': 'region',
48
+ '--exit-code': 'exitCode',
49
+ '--change-id': 'changeId',
50
+ '--log': 'log',
51
+ '--releases-max-items': 'maxItems',
52
+ '--releases-max-bytes': 'maxBytes',
53
+ };
54
+ function limitValue(name, v, fallback) {
55
+ if (v === undefined)
56
+ return fallback;
57
+ if (v === 'none')
58
+ return undefined;
59
+ if (!/^\d+$/.test(v))
60
+ throw new Error(`${name} は 0 以上の整数か none`);
61
+ return Number(v);
62
+ }
63
+ /** 引数を解釈する(誤りは Error)。`--change-id` と `--log` は空の値を許す(ラッパーの変数が空のとき) */
64
+ export function parseRecordArgs(sub, argv) {
65
+ const v = {};
66
+ const dirtyPaths = [];
67
+ let inDirty = false;
68
+ for (let i = 0; i < argv.length; i++) {
69
+ const a = argv[i];
70
+ if (a in SINGLE) {
71
+ const value = argv[++i];
72
+ const key = SINGLE[a];
73
+ const allowEmpty = key === 'changeId' || key === 'log';
74
+ if (value === undefined || (!allowEmpty && (value === '' || value.startsWith('--')))) {
75
+ throw new Error(`${a} に値が要る`);
76
+ }
77
+ v[key] = value;
78
+ inDirty = false;
79
+ }
80
+ else if (a === '--dirty-path') {
81
+ inDirty = true;
82
+ }
83
+ else if (a.startsWith('--')) {
84
+ throw new Error(`未知の引数: ${a}`);
85
+ }
86
+ else if (inDirty) {
87
+ dirtyPaths.push(a);
88
+ }
89
+ else {
90
+ throw new Error(`位置が不正な引数: ${a}`);
91
+ }
92
+ }
93
+ for (const k of ['app', 'env', 'command']) {
94
+ if (!v[k])
95
+ throw new Error(`--${k} が要る`);
96
+ }
97
+ const app = v.app;
98
+ const env = v.env;
99
+ let exitCode;
100
+ if (sub === 'finish') {
101
+ if (v.exitCode === undefined || !/^\d+$/.test(v.exitCode)) {
102
+ throw new Error('--exit-code に 0 以上の整数が要る');
103
+ }
104
+ exitCode = Number(v.exitCode);
105
+ }
106
+ return {
107
+ app,
108
+ env,
109
+ command: v.command,
110
+ repo: v.repo,
111
+ dirtyPaths,
112
+ functionName: v.functionName ?? `${app}-${env}-system-change-recorder`,
113
+ target: v.target ?? `terraform:${app}`,
114
+ region: v.region,
115
+ limit: {
116
+ maxItems: limitValue('--releases-max-items', v.maxItems),
117
+ maxBytes: limitValue('--releases-max-bytes', v.maxBytes, DEFAULT_RELEASES_MAX_BYTES),
118
+ },
119
+ exitCode,
120
+ changeId: v.changeId || undefined,
121
+ log: v.log || undefined,
122
+ };
123
+ }
124
+ /** ログを読む(読めなければ undefined。不正な UTF-8 は置き換える) */
125
+ function readLog(path) {
126
+ if (!path)
127
+ return undefined;
128
+ try {
129
+ return readFileSync(path).toString('utf8');
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ }
135
+ /** 終了の details(件数・exit_code・lambdaReleases)。destroy ではリリースを取り出さない */
136
+ export function finishDetailsOf(args) {
137
+ const exitCode = args.exitCode ?? 0;
138
+ const logText = readLog(args.log);
139
+ let releases = [];
140
+ if (args.command !== 'destroy' && logText !== undefined) {
141
+ try {
142
+ releases = extractLambdaReleases(logText);
143
+ }
144
+ catch {
145
+ releases = []; // 現行: 取り出しに失敗したら []
146
+ }
147
+ }
148
+ try {
149
+ return { ...buildFinishDetails({ exitCode, logText, releases, limit: args.limit }) };
150
+ }
151
+ catch {
152
+ return {}; // 現行: 組み立てに失敗したら {}
153
+ }
154
+ }
155
+ function record(args, result, extra, io, deps) {
156
+ const env = deps.env ?? process.env;
157
+ let payload;
158
+ try {
159
+ payload = {
160
+ kind: 'infra',
161
+ result,
162
+ target: args.target,
163
+ commit: commitOf(args.repo, args.dirtyPaths, deps.run),
164
+ actor: (deps.actor ?? actorOf)(),
165
+ // 開始と終了の両方に付ける(記録 Lambda は details を深くマージするので、同じキーが重なっても害はない)
166
+ details: { ...extra, app: args.app, env: args.env, command: args.command },
167
+ ...(args.changeId ? { changeId: args.changeId } : {}),
168
+ };
169
+ }
170
+ catch {
171
+ io.err('[change-record] ペイロードの作成に失敗しました(記録をスキップ)\n');
172
+ return;
173
+ }
174
+ sendSystemChange(payload, {
175
+ functionName: args.functionName,
176
+ region: args.region || env.AWS_REGION || 'ap-northeast-1',
177
+ dryRun: env.RECORD_DRY_RUN === '1',
178
+ warn: io.err,
179
+ run: deps.run,
180
+ });
181
+ }
182
+ /** start / finish の本体。記録の失敗・引数の誤りでも 0 を返す */
183
+ function runRecord(sub, argv, io, deps) {
184
+ const env = deps.env ?? process.env;
185
+ if (env.SKIP_CHANGE_RECORD === '1')
186
+ return 0;
187
+ let args;
188
+ try {
189
+ args = parseRecordArgs(sub, argv);
190
+ }
191
+ catch (e) {
192
+ io.err(`[change-record] 引数の誤り(記録をスキップ): ${e.message}\n`);
193
+ return 0;
194
+ }
195
+ try {
196
+ if (sub === 'start') {
197
+ const changeId = (deps.newChangeId ?? ulid)();
198
+ // 変更 ID は、記録の成否によらず出す(終了の記録に同じ ID を付ける。現行と同じ)
199
+ io.out(`${changeId}\n`);
200
+ record({ ...args, changeId }, 'started', {}, io, deps);
201
+ }
202
+ else {
203
+ const result = args.exitCode === 0 ? 'succeeded' : 'failed';
204
+ record(args, result, finishDetailsOf(args), io, deps);
205
+ }
206
+ }
207
+ catch (e) {
208
+ io.err(`[change-record] 記録に失敗しました(処理は続行): ${e.message}\n`);
209
+ }
210
+ return 0;
211
+ }
212
+ /** CLI 本体。終了コードを返す(process.exitCode は run.ts が設定する) */
213
+ export function main(argv, io, deps = {}) {
214
+ const [sub, ...rest] = argv;
215
+ // --help はサブコマンドの位置だけで見る(start / finish の引数の値の -h を拾わない)。
216
+ // USAGE は標準エラーに出す(start の標準出力は変更 ID の受け渡しに使うため、汚さない)
217
+ if (sub === '--help' || sub === '-h') {
218
+ io.err(`${USAGE}\n`);
219
+ return 0;
220
+ }
221
+ if (sub === 'start' || sub === 'finish')
222
+ return runRecord(sub, rest, io, deps);
223
+ io.err(`サブコマンドは start か finish(got: ${sub ?? 'なし'})\n\n${USAGE}\n`);
224
+ return 2;
225
+ }
226
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 終了(succeeded / failed)の記録の `details` を組み立てる(両アプリの terraform-wrapper.sh の finish_change の
3
+ * Python を移植したもの)。純粋な関数だけ(ファイルの読み取りは呼び出し側)。
4
+ */
5
+ import { type ExtractedLambdaRelease } from './releases.js';
6
+ /** `lambdaReleases` の切り詰めの既定(asanowa の現行: 3000 バイトまで。件数の上限なし) */
7
+ export declare const DEFAULT_RELEASES_MAX_BYTES = 3000;
8
+ export interface ReleasesLimit {
9
+ /** 件数の上限(undefined は無制限)。astro の現行は 30 */
10
+ maxItems?: number;
11
+ /** `lambdaReleases` の JSON(区切りの空白なし、UTF-8)のバイト数の上限(undefined は無制限)。asanowa の現行は 3000 */
12
+ maxBytes?: number;
13
+ }
14
+ export interface FinishDetails {
15
+ add?: number;
16
+ change?: number;
17
+ destroy?: number;
18
+ exit_code?: number;
19
+ lambdaReleases?: ExtractedLambdaRelease[];
20
+ lambdaReleasesOmitted?: number;
21
+ }
22
+ /** ログの最後の完了の行から、件数を取り出す(無ければ undefined) */
23
+ export declare function resourceCountsOf(logText: string): Pick<FinishDetails, 'add' | 'change' | 'destroy'> | undefined;
24
+ /**
25
+ * 失敗を先に並べ(失敗は落とさない)、上限まで残す。入らなかった件数を omitted に返す。
26
+ * 記録 Lambda の details の上限(4096 バイト)に収めるため
27
+ */
28
+ export declare function limitReleases(releases: readonly ExtractedLambdaRelease[], limit?: ReleasesLimit): {
29
+ items: ExtractedLambdaRelease[];
30
+ omitted: number;
31
+ };
32
+ /**
33
+ * 終了の details(件数・exit_code・lambdaReleases・lambdaReleasesOmitted。キーの順も現行と同じ)。
34
+ * 件数は、成功(exitCode 0)でログがあるときだけ
35
+ */
36
+ export declare function buildFinishDetails(input: {
37
+ exitCode: number;
38
+ logText?: string;
39
+ releases: readonly ExtractedLambdaRelease[];
40
+ limit?: ReleasesLimit;
41
+ }): FinishDetails;
42
+ //# sourceMappingURL=details.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"details.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/details.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,sBAAsB,EAAa,MAAM,eAAe,CAAC;AAEvE,iEAAiE;AACjE,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAMD,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,GACd,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,SAAS,CAM/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,sBAAsB,EAAE,EAC3C,KAAK,GAAE,aAAwD,GAC9D;IAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiBtD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,GAAG,aAAa,CAUhB"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 終了(succeeded / failed)の記録の `details` を組み立てる(両アプリの terraform-wrapper.sh の finish_change の
3
+ * Python を移植したもの)。純粋な関数だけ(ファイルの読み取りは呼び出し側)。
4
+ */
5
+ import { stripAnsi } from './releases.js';
6
+ /** `lambdaReleases` の切り詰めの既定(asanowa の現行: 3000 バイトまで。件数の上限なし) */
7
+ export const DEFAULT_RELEASES_MAX_BYTES = 3000;
8
+ // `Apply complete! Resources: 1 added, 2 changed, 3 destroyed.` と `Destroy complete! Resources: 3 destroyed.`
9
+ const COMPLETE = /(?:Apply|Destroy) complete! Resources: (\d+) (?:added|destroyed)(?:, (\d+) changed, (\d+) destroyed)?/g;
10
+ /** ログの最後の完了の行から、件数を取り出す(無ければ undefined) */
11
+ export function resourceCountsOf(logText) {
12
+ const ms = [...stripAnsi(logText).matchAll(COMPLETE)];
13
+ const m = ms[ms.length - 1];
14
+ if (!m)
15
+ return undefined;
16
+ if (m[2] === undefined)
17
+ return { add: 0, change: 0, destroy: Number(m[1]) }; // Destroy の短い形
18
+ return { add: Number(m[1]), change: Number(m[2]), destroy: Number(m[3]) };
19
+ }
20
+ /**
21
+ * 失敗を先に並べ(失敗は落とさない)、上限まで残す。入らなかった件数を omitted に返す。
22
+ * 記録 Lambda の details の上限(4096 バイト)に収めるため
23
+ */
24
+ export function limitReleases(releases, limit = { maxBytes: DEFAULT_RELEASES_MAX_BYTES }) {
25
+ const sorted = [
26
+ ...releases.filter((d) => d.result === 'failed'),
27
+ ...releases.filter((d) => d.result !== 'failed'),
28
+ ];
29
+ const items = [];
30
+ for (const d of sorted) {
31
+ if (limit.maxItems !== undefined && items.length >= limit.maxItems)
32
+ break;
33
+ if (limit.maxBytes !== undefined &&
34
+ Buffer.byteLength(JSON.stringify([...items, d]), 'utf8') > limit.maxBytes) {
35
+ break;
36
+ }
37
+ items.push({ target: d.target, result: d.result, change: d.change });
38
+ }
39
+ return { items, omitted: sorted.length - items.length };
40
+ }
41
+ /**
42
+ * 終了の details(件数・exit_code・lambdaReleases・lambdaReleasesOmitted。キーの順も現行と同じ)。
43
+ * 件数は、成功(exitCode 0)でログがあるときだけ
44
+ */
45
+ export function buildFinishDetails(input) {
46
+ const out = {};
47
+ if (input.exitCode === 0 && input.logText !== undefined) {
48
+ Object.assign(out, resourceCountsOf(input.logText));
49
+ }
50
+ if (input.exitCode !== 0)
51
+ out.exit_code = input.exitCode;
52
+ const { items, omitted } = limitReleases(input.releases, input.limit);
53
+ if (items.length > 0)
54
+ out.lambdaReleases = items;
55
+ if (omitted > 0)
56
+ out.lambdaReleasesOmitted = omitted;
57
+ return out;
58
+ }
59
+ //# sourceMappingURL=details.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"details.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-system-change/details.ts"],"names":[],"mappings":"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"}
@@ -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"}