@exabugs/dynamodb-client 1.16.0 → 1.17.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.
- package/CHANGELOG.md +15 -0
- package/README.md +1 -0
- package/dist/notify/channels/slack.d.ts +8 -0
- package/dist/notify/channels/slack.d.ts.map +1 -1
- package/dist/notify/channels/slack.js +27 -19
- package/dist/notify/channels/slack.js.map +1 -1
- package/dist/server/handler.cjs +3 -3
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +2 -2
- package/dist/server/media-process-handler.cjs +2 -2
- package/dist/system-changes/handler.d.ts +47 -0
- package/dist/system-changes/handler.d.ts.map +1 -0
- package/dist/system-changes/handler.js +74 -0
- package/dist/system-changes/handler.js.map +1 -0
- package/dist/system-changes/index.d.ts +15 -0
- package/dist/system-changes/index.d.ts.map +1 -0
- package/dist/system-changes/index.js +12 -0
- package/dist/system-changes/index.js.map +1 -0
- package/dist/system-changes/record.d.ts +92 -0
- package/dist/system-changes/record.d.ts.map +1 -0
- package/dist/system-changes/record.js +198 -0
- package/dist/system-changes/record.js.map +1 -0
- package/dist/system-changes/recorder.d.ts +104 -0
- package/dist/system-changes/recorder.d.ts.map +1 -0
- package/dist/system-changes/recorder.js +184 -0
- package/dist/system-changes/recorder.js.map +1 -0
- package/dist/system-changes/slack.d.ts +19 -0
- package/dist/system-changes/slack.d.ts.map +1 -0
- package/dist/system-changes/slack.js +124 -0
- package/dist/system-changes/slack.js.map +1 -0
- package/docs/RECORDS_CLIENT.md +2 -0
- package/docs/SYSTEM_CHANGES.md +117 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.17.0] - 2026-10-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`@exabugs/dynamodb-client/system-changes`**(ADR 0006 の段階 2 = (a)): システム変更履歴(`systemChanges`)の記録と Slack 通知。asanowa・astro に複製されていたロジックを 1 つにした。使い方は `docs/SYSTEM_CHANGES.md`
|
|
15
|
+
- `createSystemChangeHandler(options?)`: 記録 Lambda のハンドラのファクトリ。呼び出しごとに `withCollection` で Records に接続する。アプリ名・Records の URL・Slack の設定(SSM の名前、環境名、管理画面の URL、開始も送るか)は、オプションか環境変数(`PROJECT_NAME` / `RECORDS_IAM_URL` / `SLACK_WEBHOOK_PARAM` / `ENV_NAME` / `ADMIN_BASE_URL` / `NOTIFY_STARTED`)。書き込み先の包み方(`collection`。権限チェック層を通すアプリ用)、ログ(`log`)、SSM のクライアントを注入できる
|
|
16
|
+
- `recordSystemChange` / `recordAndNotify` / `systemChangesCollectionOf`、純粋な関数 `parseSystemChangeInput` / `toSystemChangeRecord` / `decideRunWrite` / `formatSystemChangeMessage` / `formatDuration` / `lambdaReleaseNames` / `shouldNotify`、定数・型
|
|
17
|
+
- Slack の文言は asanowa の版に揃えた(ADR 0006 の決定)。`details.lambdaReleases` の取り出しは公開関数 `lambdaReleaseNames` の 1 つ
|
|
18
|
+
- 既定の書き込み先は、`changeId` の読み取りを `find({ id }, { sort: { id: 'asc' }, limit: 1 })`(Records の ID の経路。PK/SK の Query 1 回) で行う(`findOne` の 404 で Records が ITEM_NOT_FOUND の warn を出さない)
|
|
19
|
+
- **`postSlackText(url, text, options?)`**(`src/notify/channels/slack.ts`。内部用、notify の index からは出さない): `createSlackChannel` の送信処理を切り出したもの。`system-changes` が使う
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `createSlackChannel` の送信は、切り出した `postSlackText` を呼ぶ。挙動は同じ(URL の接頭辞の確認、timeout 既定 5 秒、URL を出さないエラー)。違いは、`fetchImpl` を省略したときの `fetch` を、チャネルの作成時ではなく送信のたびに読むことだけ
|
|
24
|
+
|
|
10
25
|
## [1.16.0] - 2026-10-20
|
|
11
26
|
|
|
12
27
|
### Added
|
package/README.md
CHANGED
|
@@ -198,6 +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
202
|
- **[Deployment](docs/DEPLOYMENT.md)** - Production deployment guide
|
|
202
203
|
- **[Terraform Modules](terraform/README.md)** - Infrastructure as Code
|
|
203
204
|
|
|
@@ -8,5 +8,13 @@ export interface SlackOptions {
|
|
|
8
8
|
fetchImpl?: typeof fetch;
|
|
9
9
|
timeoutMs?: number;
|
|
10
10
|
}
|
|
11
|
+
/** `postSlackText` のオプション(`createSlackChannel` と共通) */
|
|
12
|
+
export type SlackPostOptions = Pick<SlackOptions, 'urlPrefix' | 'fetchImpl' | 'timeoutMs'>;
|
|
13
|
+
/**
|
|
14
|
+
* 文字列(mrkdwn)を 1 通、Webhook に送る。失敗は例外(メッセージに URL を出さない)。
|
|
15
|
+
* `createSlackChannel` の送信と同じ処理(URL の接頭辞の確認、timeout 既定 5 秒)。
|
|
16
|
+
* Notice を経由しない直接の投稿(システム変更履歴など)に使う
|
|
17
|
+
*/
|
|
18
|
+
export declare function postSlackText(url: string, text: string, options?: SlackPostOptions): Promise<void>;
|
|
11
19
|
export declare function createSlackChannel(options: SlackOptions): Channel;
|
|
12
20
|
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/notify/channels/slack.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQnD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAKnD;AAED,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/notify/channels/slack.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQnD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAKnD;AAED,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC;AAE3F;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAUjE"}
|
|
@@ -13,8 +13,33 @@ export function slackText(notices) {
|
|
|
13
13
|
parts.push(`…ほか ${notices.length - shown.length} 件`);
|
|
14
14
|
return parts.join('\n\n');
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* 文字列(mrkdwn)を 1 通、Webhook に送る。失敗は例外(メッセージに URL を出さない)。
|
|
18
|
+
* `createSlackChannel` の送信と同じ処理(URL の接頭辞の確認、timeout 既定 5 秒)。
|
|
19
|
+
* Notice を経由しない直接の投稿(システム変更履歴など)に使う
|
|
20
|
+
*/
|
|
21
|
+
export async function postSlackText(url, text, options = {}) {
|
|
22
|
+
if (!url.startsWith(options.urlPrefix ?? 'https://hooks.slack.com/')) {
|
|
23
|
+
throw new Error('Slack の Webhook の URL が不正です(許可した接頭辞で始まっていません)');
|
|
24
|
+
}
|
|
17
25
|
const doFetch = options.fetchImpl ?? fetch;
|
|
26
|
+
let res;
|
|
27
|
+
try {
|
|
28
|
+
res = await doFetch(url, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
body: JSON.stringify({ text }),
|
|
32
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5000),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
// fetch のエラーには URL(秘密)が入ることがあるので、原因の種類だけを残す
|
|
37
|
+
throw new Error(`Slack への送信に失敗しました (${e instanceof Error ? e.name : 'error'})`);
|
|
38
|
+
}
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
throw new Error(`Slack への送信に失敗しました (HTTP ${res.status})`);
|
|
41
|
+
}
|
|
42
|
+
export function createSlackChannel(options) {
|
|
18
43
|
return {
|
|
19
44
|
name: 'slack',
|
|
20
45
|
needsRecipients: false,
|
|
@@ -22,24 +47,7 @@ export function createSlackChannel(options) {
|
|
|
22
47
|
if (notices.length === 0)
|
|
23
48
|
return;
|
|
24
49
|
const url = await options.getWebhookUrl();
|
|
25
|
-
|
|
26
|
-
throw new Error('Slack の Webhook の URL が不正です(許可した接頭辞で始まっていません)');
|
|
27
|
-
}
|
|
28
|
-
let res;
|
|
29
|
-
try {
|
|
30
|
-
res = await doFetch(url, {
|
|
31
|
-
method: 'POST',
|
|
32
|
-
headers: { 'Content-Type': 'application/json' },
|
|
33
|
-
body: JSON.stringify({ text: slackText(notices) }),
|
|
34
|
-
signal: AbortSignal.timeout(options.timeoutMs ?? 5000),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
catch (e) {
|
|
38
|
-
// fetch のエラーには URL(秘密)が入ることがあるので、原因の種類だけを残す
|
|
39
|
-
throw new Error(`Slack への送信に失敗しました (${e instanceof Error ? e.name : 'error'})`);
|
|
40
|
-
}
|
|
41
|
-
if (!res.ok)
|
|
42
|
-
throw new Error(`Slack への送信に失敗しました (HTTP ${res.status})`);
|
|
50
|
+
await postSlackText(url, slackText(notices), options);
|
|
43
51
|
},
|
|
44
52
|
};
|
|
45
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/notify/channels/slack.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGvD,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,kCAAkC;AAClC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAChC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAEvE,MAAM,UAAU,SAAS,CAAC,OAAiB;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvF,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IACxF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/notify/channels/slack.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGvD,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,kCAAkC;AAClC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAChC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAEvE,MAAM,UAAU,SAAS,CAAC,OAAiB;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvF,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IACxF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAcD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,IAAY,EACZ,UAA4B,EAAE;IAE9B,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,0BAA0B,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC3C,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YACvB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;YAC9B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;SACvD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,4CAA4C;QAC5C,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,eAAe,EAAE,KAAK;QACtB,KAAK,CAAC,IAAI,CAAC,OAAO;YAChB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACjC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1C,MAAM,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QACxD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/server/handler.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// @exabugs/dynamodb-client v1.
|
|
2
|
-
// Built: 2026-10-
|
|
1
|
+
// @exabugs/dynamodb-client v1.17.0
|
|
2
|
+
// Built: 2026-10-24T03:15:07.224Z
|
|
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.
|
|
38983
|
+
const version = "1.17.0";
|
|
38984
38984
|
return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
|
|
38985
38985
|
}
|
|
38986
38986
|
if (event.requestContext.http.method !== "POST") {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* システム変更履歴の記録 Lambda のハンドラのファクトリ(ADR 0006 の (a))。
|
|
3
|
+
*
|
|
4
|
+
* 直接呼び出し(`aws lambda invoke`。terraform のラッパー・人・CI)で使う。入力と応答は docs/SYSTEM_CHANGES.md。
|
|
5
|
+
* 呼び出しごとに Records(IAM 版 Function URL)のクライアントを作り、終わったら閉じる(`withCollection`)。
|
|
6
|
+
*
|
|
7
|
+
* 設定は、オプションが環境変数より優先される(どちらも呼び出しのたびに読む):
|
|
8
|
+
* - `app`: PROJECT_NAME(必須)
|
|
9
|
+
* - `records.url` / `records.region`: RECORDS_IAM_URL(必須)/ AWS_REGION
|
|
10
|
+
* - Slack(任意。webhookParam が無ければ送らない): SLACK_WEBHOOK_PARAM(SSM の SecureString の名前)/ ENV_NAME /
|
|
11
|
+
* ADMIN_BASE_URL / NOTIFY_STARTED('true' で開始も送る)
|
|
12
|
+
*
|
|
13
|
+
* 失敗の扱い: 入力の誤り・Records への書き込みの失敗は、構造化ログ(warn / error)に出し、例外にする(呼び出し元が
|
|
14
|
+
* `FunctionError` で分かるように)。Slack への送信の失敗は、記録を失敗にしない。
|
|
15
|
+
*/
|
|
16
|
+
import { SSMClient } from '@aws-sdk/client-ssm';
|
|
17
|
+
import { type RecordsClientOptions, type RecordsCollection } from '../client/recordsClient.js';
|
|
18
|
+
import { type StoredSystemChange } from './record.js';
|
|
19
|
+
import { type SystemChangeLog, type SystemChangeResponse, type SystemChangeSlackOptions, type SystemChangesCollection } from './recorder.js';
|
|
20
|
+
export interface SystemChangeHandlerOptions {
|
|
21
|
+
/** アプリ名。省略時は環境変数 PROJECT_NAME */
|
|
22
|
+
app?: string;
|
|
23
|
+
/** Records のクライアント。url / region は省略時に環境変数。timeout の既定は RECORD_HTTP_TIMEOUT_MS(5 秒) */
|
|
24
|
+
records?: RecordsClientOptions;
|
|
25
|
+
/**
|
|
26
|
+
* 書き込み先を作る(既定 systemChangesCollectionOf)。権限チェック層(astro の authorizedCollection など)を
|
|
27
|
+
* 通すアプリは、受け取った生のコレクションを自分の層で包んで返す
|
|
28
|
+
*/
|
|
29
|
+
collection?: (raw: RecordsCollection<StoredSystemChange>) => SystemChangesCollection;
|
|
30
|
+
/**
|
|
31
|
+
* Slack の設定。各項目は省略時に環境変数(上記)。getWebhookUrl の既定は SSM(SecureString。実行環境の間は
|
|
32
|
+
* キャッシュ)。false で送らない
|
|
33
|
+
*/
|
|
34
|
+
slack?: Partial<SystemChangeSlackOptions> | false;
|
|
35
|
+
/** SSM のクライアント(既定は AWS_REGION の SSMClient。テスト用) */
|
|
36
|
+
ssmClient?: Pick<SSMClient, 'send'>;
|
|
37
|
+
/** ログの出力先(既定は notify の logJson) */
|
|
38
|
+
log?: SystemChangeLog;
|
|
39
|
+
/** 現在時刻(テスト用) */
|
|
40
|
+
now?: () => Date;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 記録 Lambda のハンドラを作る。アプリのエントリは、これを export するだけ:
|
|
44
|
+
* `export const handler = createSystemChangeHandler();`
|
|
45
|
+
*/
|
|
46
|
+
export declare function createSystemChangeHandler(options?: SystemChangeHandlerOptions): (event: unknown) => Promise<SystemChangeResponse>;
|
|
47
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/system-changes/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAuB,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAEvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAG7B,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,0BAA0B;IACzC,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,KAAK,uBAAuB,CAAC;IACrF;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,KAAK,CAAC;IAClD,mDAAmD;IACnD,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACpC,mCAAmC;IACnC,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,iBAAiB;IACjB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAmCD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAwBnD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* システム変更履歴の記録 Lambda のハンドラのファクトリ(ADR 0006 の (a))。
|
|
3
|
+
*
|
|
4
|
+
* 直接呼び出し(`aws lambda invoke`。terraform のラッパー・人・CI)で使う。入力と応答は docs/SYSTEM_CHANGES.md。
|
|
5
|
+
* 呼び出しごとに Records(IAM 版 Function URL)のクライアントを作り、終わったら閉じる(`withCollection`)。
|
|
6
|
+
*
|
|
7
|
+
* 設定は、オプションが環境変数より優先される(どちらも呼び出しのたびに読む):
|
|
8
|
+
* - `app`: PROJECT_NAME(必須)
|
|
9
|
+
* - `records.url` / `records.region`: RECORDS_IAM_URL(必須)/ AWS_REGION
|
|
10
|
+
* - Slack(任意。webhookParam が無ければ送らない): SLACK_WEBHOOK_PARAM(SSM の SecureString の名前)/ ENV_NAME /
|
|
11
|
+
* ADMIN_BASE_URL / NOTIFY_STARTED('true' で開始も送る)
|
|
12
|
+
*
|
|
13
|
+
* 失敗の扱い: 入力の誤り・Records への書き込みの失敗は、構造化ログ(warn / error)に出し、例外にする(呼び出し元が
|
|
14
|
+
* `FunctionError` で分かるように)。Slack への送信の失敗は、記録を失敗にしない。
|
|
15
|
+
*/
|
|
16
|
+
import { GetParameterCommand, SSMClient } from '@aws-sdk/client-ssm';
|
|
17
|
+
import { withCollection, } from '../client/recordsClient.js';
|
|
18
|
+
import { SYSTEM_CHANGES_RESOURCE } from './record.js';
|
|
19
|
+
import { RECORD_HTTP_TIMEOUT_MS, recordAndNotify, systemChangesCollectionOf, } from './recorder.js';
|
|
20
|
+
/** SSM(SecureString)から webhook の URL を取る関数。param ごとに、実行環境の間は使い回す */
|
|
21
|
+
function ssmWebhookGetter(client) {
|
|
22
|
+
const cache = new Map();
|
|
23
|
+
return async (param) => {
|
|
24
|
+
const hit = cache.get(param);
|
|
25
|
+
if (hit)
|
|
26
|
+
return hit;
|
|
27
|
+
const r = await client().send(new GetParameterCommand({ Name: param, WithDecryption: true }));
|
|
28
|
+
const url = r.Parameter?.Value;
|
|
29
|
+
if (!url)
|
|
30
|
+
throw new Error('webhook の値が空です');
|
|
31
|
+
cache.set(param, url);
|
|
32
|
+
return url;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const envOf = (name) => process.env[name] || undefined;
|
|
36
|
+
/** Slack の設定を、オプションと環境変数から決める */
|
|
37
|
+
function slackOf(options, defaultGetter) {
|
|
38
|
+
if (options.slack === false)
|
|
39
|
+
return undefined;
|
|
40
|
+
const s = options.slack ?? {};
|
|
41
|
+
return {
|
|
42
|
+
...s,
|
|
43
|
+
webhookParam: s.webhookParam ?? envOf('SLACK_WEBHOOK_PARAM'),
|
|
44
|
+
env: s.env ?? envOf('ENV_NAME'),
|
|
45
|
+
adminBaseUrl: s.adminBaseUrl ?? envOf('ADMIN_BASE_URL'),
|
|
46
|
+
notifyStarted: s.notifyStarted ?? process.env.NOTIFY_STARTED === 'true',
|
|
47
|
+
getWebhookUrl: s.getWebhookUrl ?? defaultGetter,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 記録 Lambda のハンドラを作る。アプリのエントリは、これを export するだけ:
|
|
52
|
+
* `export const handler = createSystemChangeHandler();`
|
|
53
|
+
*/
|
|
54
|
+
export function createSystemChangeHandler(options = {}) {
|
|
55
|
+
let ssm = options.ssmClient;
|
|
56
|
+
const getFromSsm = ssmWebhookGetter(() => (ssm ??= new SSMClient({ region: options.records?.region ?? process.env.AWS_REGION })));
|
|
57
|
+
const wrap = options.collection ?? systemChangesCollectionOf;
|
|
58
|
+
return async (event) => {
|
|
59
|
+
const app = options.app ?? envOf('PROJECT_NAME');
|
|
60
|
+
if (!app)
|
|
61
|
+
throw new Error('PROJECT_NAME environment variable is required');
|
|
62
|
+
const url = options.records?.url ?? envOf('RECORDS_IAM_URL');
|
|
63
|
+
if (!url)
|
|
64
|
+
throw new Error('RECORDS_IAM_URL environment variable is required');
|
|
65
|
+
const records = {
|
|
66
|
+
...options.records,
|
|
67
|
+
url,
|
|
68
|
+
timeout: options.records?.timeout ?? RECORD_HTTP_TIMEOUT_MS,
|
|
69
|
+
};
|
|
70
|
+
const slack = slackOf(options, getFromSsm);
|
|
71
|
+
return withCollection(SYSTEM_CHANGES_RESOURCE, (raw) => recordAndNotify(wrap(raw), event, { app, slack, log: options.log, now: options.now }), records);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/system-changes/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAGL,cAAc,GACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAA2B,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,sBAAsB,EAKtB,eAAe,EACf,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AAyBvB,oEAAoE;AACpE,SAAS,gBAAgB,CAAC,MAAqC;IAC7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,OAAO,KAAK,EAAE,KAAa,EAAmB,EAAE;QAC9C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;QACpB,MAAM,CAAC,GAAG,MAAM,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC;QAC/B,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC5C,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtB,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,IAAY,EAAsB,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;AAEnF,iCAAiC;AACjC,SAAS,OAAO,CACd,OAAmC,EACnC,aAAiD;IAEjD,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAC9B,OAAO;QACL,GAAG,CAAC;QACJ,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC,qBAAqB,CAAC;QAC5D,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,UAAU,CAAC;QAC/B,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC,gBAAgB,CAAC;QACvD,aAAa,EAAE,CAAC,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM;QACvE,aAAa,EAAE,CAAC,CAAC,aAAa,IAAI,aAAa;KAChD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAAsC,EAAE;IAExC,IAAI,GAAG,GAAwC,OAAO,CAAC,SAAS,CAAC;IACjE,MAAM,UAAU,GAAG,gBAAgB,CACjC,GAAG,EAAE,CAAC,CAAC,GAAG,KAAK,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAC7F,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,yBAAyB,CAAC;IAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC7D,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAyB;YACpC,GAAG,OAAO,CAAC,OAAO;YAClB,GAAG;YACH,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,IAAI,sBAAsB;SAC5D,CAAC;QACF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC3C,OAAO,cAAc,CACnB,uBAAuB,EACvB,CAAC,GAAG,EAAE,EAAE,CACN,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,EACvF,OAAO,CACR,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* システム変更履歴(`@exabugs/dynamodb-client/system-changes`。ADR 0006 の (a))
|
|
3
|
+
*
|
|
4
|
+
* 環境に起きたシステム変更(インフラの変更・Lambda のリリース・アプリのリリース・OTA)を、Records の
|
|
5
|
+
* `systemChanges` に記録し、結果を Slack に送る部品。記録 Lambda のハンドラのファクトリも持つ。
|
|
6
|
+
* アプリが渡すのは設定(アプリ名、SSM の名前、管理画面の URL、書き込み先の包み方)だけ。docs/SYSTEM_CHANGES.md
|
|
7
|
+
*/
|
|
8
|
+
export { createSystemChangeHandler } from './handler.js';
|
|
9
|
+
export type { SystemChangeHandlerOptions } from './handler.js';
|
|
10
|
+
export { CHANGE_ID_PATTERN, LIMITS, SYSTEM_CHANGES_RESOURCE, SYSTEM_CHANGE_KINDS, SYSTEM_CHANGE_RESULTS, SystemChangeInputError, decideRunWrite, isFinished, parseSystemChangeInput, toSystemChangeRecord, } from './record.js';
|
|
11
|
+
export type { RunDecision, StoredSystemChange, SystemChangeInput, SystemChangeKind, SystemChangeRecord, SystemChangeResult, } from './record.js';
|
|
12
|
+
export { RECORD_HTTP_TIMEOUT_MS, SLACK_HTTP_TIMEOUT_MS, recordAndNotify, recordSystemChange, systemChangesCollectionOf, } from './recorder.js';
|
|
13
|
+
export type { RecordAndNotifyOptions, RecordOutcome, RecordSystemChangeOptions, SystemChangeLog, SystemChangeLogLevel, SystemChangeResponse, SystemChangeSlackOptions, SystemChangesCollection, } from './recorder.js';
|
|
14
|
+
export { formatDuration, formatSystemChangeMessage, lambdaReleaseNames, shouldNotify, } from './slack.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/system-changes/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,UAAU,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,aAAa,EACb,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* システム変更履歴(`@exabugs/dynamodb-client/system-changes`。ADR 0006 の (a))
|
|
3
|
+
*
|
|
4
|
+
* 環境に起きたシステム変更(インフラの変更・Lambda のリリース・アプリのリリース・OTA)を、Records の
|
|
5
|
+
* `systemChanges` に記録し、結果を Slack に送る部品。記録 Lambda のハンドラのファクトリも持つ。
|
|
6
|
+
* アプリが渡すのは設定(アプリ名、SSM の名前、管理画面の URL、書き込み先の包み方)だけ。docs/SYSTEM_CHANGES.md
|
|
7
|
+
*/
|
|
8
|
+
export { createSystemChangeHandler } from './handler.js';
|
|
9
|
+
export { CHANGE_ID_PATTERN, LIMITS, SYSTEM_CHANGES_RESOURCE, SYSTEM_CHANGE_KINDS, SYSTEM_CHANGE_RESULTS, SystemChangeInputError, decideRunWrite, isFinished, parseSystemChangeInput, toSystemChangeRecord, } from './record.js';
|
|
10
|
+
export { RECORD_HTTP_TIMEOUT_MS, SLACK_HTTP_TIMEOUT_MS, recordAndNotify, recordSystemChange, systemChangesCollectionOf, } from './recorder.js';
|
|
11
|
+
export { formatDuration, formatSystemChangeMessage, lambdaReleaseNames, shouldNotify, } from './slack.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/system-changes/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,UAAU,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AASrB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AAWvB,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** リソース名(Records のコレクション名。管理画面の URL〔/systemChanges/{id}/show〕にも使う) */
|
|
2
|
+
export declare const SYSTEM_CHANGES_RESOURCE = "systemChanges";
|
|
3
|
+
/** 種別(許可リスト)。管理画面のラベル・Slack の文言(slack.ts の KIND_LABEL)と揃える */
|
|
4
|
+
export declare const SYSTEM_CHANGE_KINDS: readonly ["app-release", "ota", "infra", "lambda-release"];
|
|
5
|
+
/** 結果(許可リスト) */
|
|
6
|
+
export declare const SYSTEM_CHANGE_RESULTS: readonly ["started", "succeeded", "failed"];
|
|
7
|
+
export type SystemChangeKind = (typeof SYSTEM_CHANGE_KINDS)[number];
|
|
8
|
+
export type SystemChangeResult = (typeof SYSTEM_CHANGE_RESULTS)[number];
|
|
9
|
+
/** 文字列の長さの上限(超えた分は切り、末尾に … を付ける) */
|
|
10
|
+
export declare const LIMITS: {
|
|
11
|
+
readonly target: 200;
|
|
12
|
+
readonly version: 100;
|
|
13
|
+
readonly commit: 64;
|
|
14
|
+
readonly actor: 200;
|
|
15
|
+
/** details を JSON にしたときのバイト数の上限(超えたら拒否する。構造を壊して切らない) */
|
|
16
|
+
readonly detailsBytes: 4096;
|
|
17
|
+
};
|
|
18
|
+
/** 変更 ID の形式(ULID: Crockford の base32 の 26 文字。Records が付ける id と同じ形) */
|
|
19
|
+
export declare const CHANGE_ID_PATTERN: RegExp;
|
|
20
|
+
export interface SystemChangeInput {
|
|
21
|
+
kind: SystemChangeKind;
|
|
22
|
+
result: SystemChangeResult;
|
|
23
|
+
target: string;
|
|
24
|
+
changeId?: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
commit?: string;
|
|
27
|
+
actor?: string;
|
|
28
|
+
details?: Record<string, unknown>;
|
|
29
|
+
occurredAt?: string;
|
|
30
|
+
}
|
|
31
|
+
/** 保存する項目(changeId が無ければ、id は Records が ULID で付ける) */
|
|
32
|
+
export interface SystemChangeRecord {
|
|
33
|
+
kind: SystemChangeKind;
|
|
34
|
+
result: SystemChangeResult;
|
|
35
|
+
target: string;
|
|
36
|
+
/** 並べ替え用。changeId のレコードでは、最初の記録の時刻のまま(更新で変えない) */
|
|
37
|
+
occurredAt: string;
|
|
38
|
+
version?: string;
|
|
39
|
+
commit?: string;
|
|
40
|
+
actor?: string;
|
|
41
|
+
details?: Record<string, unknown>;
|
|
42
|
+
/** changeId のレコードだけ: 開始の時刻(started で作ったとき) */
|
|
43
|
+
startedAt?: string;
|
|
44
|
+
/** changeId のレコードだけ: 終了の時刻(終了の記録の時刻) */
|
|
45
|
+
finishedAt?: string;
|
|
46
|
+
/** changeId のレコードだけ: finishedAt − startedAt(ミリ秒。startedAt が無ければ付けない) */
|
|
47
|
+
durationMs?: number;
|
|
48
|
+
}
|
|
49
|
+
/** 読み取った記録(Records が付ける id・タイムスタンプを含む) */
|
|
50
|
+
export type StoredSystemChange = SystemChangeRecord & {
|
|
51
|
+
id: string;
|
|
52
|
+
} & Record<string, unknown>;
|
|
53
|
+
/** 入力の誤り(呼び出し元の誤り。再試行しても直らない) */
|
|
54
|
+
export declare class SystemChangeInputError extends Error {
|
|
55
|
+
constructor(message: string);
|
|
56
|
+
}
|
|
57
|
+
export declare const isPlainObject: (v: unknown) => v is Record<string, unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* 入力を検証し、保存する項目にする(純粋な関数)。誤りは SystemChangeInputError。
|
|
60
|
+
* 任意の文字列は、空(空白だけを含む)なら項目ごと省く(null は保存しない)
|
|
61
|
+
*/
|
|
62
|
+
export declare function toSystemChangeRecord(input: unknown, now?: () => Date): SystemChangeRecord;
|
|
63
|
+
/** 入力を検証し、保存する項目と、変更 ID(あれば)にする(純粋な関数)。誤りは SystemChangeInputError */
|
|
64
|
+
export declare function parseSystemChangeInput(input: unknown, now?: () => Date): {
|
|
65
|
+
record: SystemChangeRecord;
|
|
66
|
+
changeId?: string;
|
|
67
|
+
};
|
|
68
|
+
/** 終わった状態(succeeded / failed) */
|
|
69
|
+
export declare const isFinished: (r: SystemChangeResult) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* changeId の記録の判定(純粋な関数)。既存のレコード(無ければ null)と入力から、書き込みを決める。
|
|
72
|
+
* - (a) 無い → 作成(started なら startedAt、終了なら finishedAt = occurredAt)
|
|
73
|
+
* - (b) 既存が started、入力が終了 → 終了に更新(result・finishedAt・durationMs と、入力にある項目。occurredAt・kind は変えない)
|
|
74
|
+
* - (c) 既存が終了、入力が started → 無視(状態を戻さない。遅れて届いた開始)
|
|
75
|
+
* - (d) 既存が終了、入力が同じ終了 → 無視(再送。書き直さない = 冪等)
|
|
76
|
+
* - (e) 既存が終了、入力が違う終了 → 無視(最初の終了を優先)。conflict として warn
|
|
77
|
+
* - (f) 既存が started、入力も started → 無視(再送)
|
|
78
|
+
* 応答の occurredAt は、レコードの並べ替えの時刻(作成なら入力、既存なら既存の値)
|
|
79
|
+
*/
|
|
80
|
+
export type RunDecision = {
|
|
81
|
+
action: 'create';
|
|
82
|
+
set: SystemChangeRecord;
|
|
83
|
+
} | {
|
|
84
|
+
action: 'finish';
|
|
85
|
+
set: Partial<SystemChangeRecord>;
|
|
86
|
+
merged: SystemChangeRecord;
|
|
87
|
+
} | {
|
|
88
|
+
action: 'ignore';
|
|
89
|
+
reason: 'stale-start' | 'duplicate-finish' | 'conflict' | 'duplicate-start';
|
|
90
|
+
};
|
|
91
|
+
export declare function decideRunWrite(existing: StoredSystemChange | null, record: SystemChangeRecord): RunDecision;
|
|
92
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/system-changes/record.ts"],"names":[],"mappings":"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"}
|