@exabugs/dynamodb-client 1.15.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +2 -0
  3. package/dist/client/index.iam.d.ts +1 -0
  4. package/dist/client/index.iam.d.ts.map +1 -1
  5. package/dist/client/index.iam.js +1 -0
  6. package/dist/client/index.iam.js.map +1 -1
  7. package/dist/client/recordsClient.d.ts +51 -0
  8. package/dist/client/recordsClient.d.ts.map +1 -0
  9. package/dist/client/recordsClient.js +64 -0
  10. package/dist/client/recordsClient.js.map +1 -0
  11. package/dist/notify/admins.d.ts +44 -0
  12. package/dist/notify/admins.d.ts.map +1 -0
  13. package/dist/notify/admins.js +55 -0
  14. package/dist/notify/admins.js.map +1 -0
  15. package/dist/notify/channels/slack.d.ts +8 -0
  16. package/dist/notify/channels/slack.d.ts.map +1 -1
  17. package/dist/notify/channels/slack.js +27 -19
  18. package/dist/notify/channels/slack.js.map +1 -1
  19. package/dist/notify/index.d.ts +2 -0
  20. package/dist/notify/index.d.ts.map +1 -1
  21. package/dist/notify/index.js +1 -0
  22. package/dist/notify/index.js.map +1 -1
  23. package/dist/server/handler.cjs +3 -3
  24. package/dist/server/media/recordsClient.d.ts.map +1 -1
  25. package/dist/server/media/recordsClient.js +10 -30
  26. package/dist/server/media/recordsClient.js.map +1 -1
  27. package/dist/server/media-handler.cjs +2 -2
  28. package/dist/server/media-process-handler-dlq.cjs +46 -25
  29. package/dist/server/media-process-handler-dlq.cjs.map +4 -4
  30. package/dist/server/media-process-handler.cjs +46 -25
  31. package/dist/server/media-process-handler.cjs.map +4 -4
  32. package/dist/system-changes/handler.d.ts +47 -0
  33. package/dist/system-changes/handler.d.ts.map +1 -0
  34. package/dist/system-changes/handler.js +74 -0
  35. package/dist/system-changes/handler.js.map +1 -0
  36. package/dist/system-changes/index.d.ts +15 -0
  37. package/dist/system-changes/index.d.ts.map +1 -0
  38. package/dist/system-changes/index.js +12 -0
  39. package/dist/system-changes/index.js.map +1 -0
  40. package/dist/system-changes/record.d.ts +92 -0
  41. package/dist/system-changes/record.d.ts.map +1 -0
  42. package/dist/system-changes/record.js +198 -0
  43. package/dist/system-changes/record.js.map +1 -0
  44. package/dist/system-changes/recorder.d.ts +104 -0
  45. package/dist/system-changes/recorder.d.ts.map +1 -0
  46. package/dist/system-changes/recorder.js +184 -0
  47. package/dist/system-changes/recorder.js.map +1 -0
  48. package/dist/system-changes/slack.d.ts +19 -0
  49. package/dist/system-changes/slack.d.ts.map +1 -0
  50. package/dist/system-changes/slack.js +124 -0
  51. package/dist/system-changes/slack.js.map +1 -0
  52. package/docs/RECORDS_CLIENT.md +47 -0
  53. package/docs/SYSTEM_CHANGES.md +117 -0
  54. package/package.json +8 -2
  55. package/terraform/media/README.md +1 -2
  56. package/terraform/media/variables.tf +0 -37
@@ -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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"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * システム変更履歴(systemChanges)の入力の検証と、変更(run)の遷移の判定(ADR 0006 の (a)。純粋な関数だけ)。
3
+ *
4
+ * asanowa(functions/ops-notify/src/system-change-recorder.ts。ADR-0044)と astro(apps/server/src/system-changes/record.ts。
5
+ * ADR-0011)に複製されていたものを、1 つにした。入力の形と遷移の規則は docs/SYSTEM_CHANGES.md。
6
+ */
7
+ import { cleanLine } from '../notify/policy.js';
8
+ /** リソース名(Records のコレクション名。管理画面の URL〔/systemChanges/{id}/show〕にも使う) */
9
+ export const SYSTEM_CHANGES_RESOURCE = 'systemChanges';
10
+ /** 種別(許可リスト)。管理画面のラベル・Slack の文言(slack.ts の KIND_LABEL)と揃える */
11
+ export const SYSTEM_CHANGE_KINDS = ['app-release', 'ota', 'infra', 'lambda-release'];
12
+ /** 結果(許可リスト) */
13
+ export const SYSTEM_CHANGE_RESULTS = ['started', 'succeeded', 'failed'];
14
+ /** 文字列の長さの上限(超えた分は切り、末尾に … を付ける) */
15
+ export const LIMITS = {
16
+ target: 200,
17
+ version: 100,
18
+ commit: 64,
19
+ actor: 200,
20
+ /** details を JSON にしたときのバイト数の上限(超えたら拒否する。構造を壊して切らない) */
21
+ detailsBytes: 4096,
22
+ };
23
+ /** 入力のキー(これ以外のキーは、綴りの誤りとして拒否する) */
24
+ const INPUT_KEYS = new Set([
25
+ 'kind',
26
+ 'result',
27
+ 'target',
28
+ 'version',
29
+ 'commit',
30
+ 'actor',
31
+ 'details',
32
+ 'occurredAt',
33
+ 'notify',
34
+ 'changeId',
35
+ ]);
36
+ /** 変更 ID の形式(ULID: Crockford の base32 の 26 文字。Records が付ける id と同じ形) */
37
+ export const CHANGE_ID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
38
+ /** 入力の誤り(呼び出し元の誤り。再試行しても直らない) */
39
+ export class SystemChangeInputError extends Error {
40
+ constructor(message) {
41
+ super(message);
42
+ this.name = 'SystemChangeInputError';
43
+ }
44
+ }
45
+ /** ISO 8601(日時・タイムゾーン付き)。日付だけ・タイムゾーンなしは、解釈がずれるので拒否する */
46
+ const ISO_8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})$/;
47
+ export const isPlainObject = (v) => typeof v === 'object' &&
48
+ v !== null &&
49
+ !Array.isArray(v) &&
50
+ Object.getPrototypeOf(v) === Object.prototype;
51
+ /**
52
+ * 入力を検証し、保存する項目にする(純粋な関数)。誤りは SystemChangeInputError。
53
+ * 任意の文字列は、空(空白だけを含む)なら項目ごと省く(null は保存しない)
54
+ */
55
+ export function toSystemChangeRecord(input, now = () => new Date()) {
56
+ return parseSystemChangeInput(input, now).record;
57
+ }
58
+ /** 入力を検証し、保存する項目と、変更 ID(あれば)にする(純粋な関数)。誤りは SystemChangeInputError */
59
+ export function parseSystemChangeInput(input, now = () => new Date()) {
60
+ if (!isPlainObject(input))
61
+ throw new SystemChangeInputError('入力はオブジェクトにしてください');
62
+ const unknownKeys = Object.keys(input).filter((k) => !INPUT_KEYS.has(k));
63
+ if (unknownKeys.length > 0) {
64
+ throw new SystemChangeInputError(`不明な項目です: ${unknownKeys.join(', ').slice(0, 200)}`);
65
+ }
66
+ if (input.notify !== undefined && typeof input.notify !== 'boolean') {
67
+ throw new SystemChangeInputError('notify は true / false にしてください');
68
+ }
69
+ const { kind, result } = input;
70
+ if (typeof kind !== 'string' || !SYSTEM_CHANGE_KINDS.includes(kind)) {
71
+ throw new SystemChangeInputError(`kind は ${SYSTEM_CHANGE_KINDS.join(' / ')} のどれかです`);
72
+ }
73
+ if (typeof result !== 'string' ||
74
+ !SYSTEM_CHANGE_RESULTS.includes(result)) {
75
+ throw new SystemChangeInputError(`result は ${SYSTEM_CHANGE_RESULTS.join(' / ')} のどれかです`);
76
+ }
77
+ if (typeof input.target !== 'string')
78
+ throw new SystemChangeInputError('target は必須です');
79
+ const target = cleanLine(input.target, LIMITS.target);
80
+ if (!target)
81
+ throw new SystemChangeInputError('target が空です');
82
+ const optional = (name) => {
83
+ const v = input[name];
84
+ if (v === undefined || v === null)
85
+ return undefined;
86
+ if (typeof v !== 'string')
87
+ throw new SystemChangeInputError(`${name} は文字列にしてください`);
88
+ return cleanLine(v, LIMITS[name]);
89
+ };
90
+ let changeId;
91
+ if (input.changeId !== undefined && input.changeId !== null) {
92
+ if (typeof input.changeId !== 'string' || !CHANGE_ID_PATTERN.test(input.changeId)) {
93
+ throw new SystemChangeInputError('changeId は ULID の形式(0-9 と A-Z〔I・L・O・U を除く〕の大文字 26 文字)にしてください');
94
+ }
95
+ changeId = input.changeId;
96
+ }
97
+ let occurredAt;
98
+ if (input.occurredAt === undefined || input.occurredAt === null) {
99
+ occurredAt = now().toISOString();
100
+ }
101
+ else {
102
+ const t = input.occurredAt;
103
+ if (typeof t !== 'string' || !ISO_8601.test(t) || Number.isNaN(Date.parse(t))) {
104
+ throw new SystemChangeInputError('occurredAt は ISO 8601 の日時(例: 2026-10-16T09:00:00+09:00)にしてください');
105
+ }
106
+ // UTC の文字列にそろえる(並べ替えは文字列の順なので、タイムゾーンが混ざると順序がずれる)
107
+ occurredAt = new Date(t).toISOString();
108
+ }
109
+ let details;
110
+ if (input.details !== undefined && input.details !== null) {
111
+ if (!isPlainObject(input.details)) {
112
+ throw new SystemChangeInputError('details はオブジェクトにしてください');
113
+ }
114
+ const json = JSON.stringify(input.details);
115
+ if (Buffer.byteLength(json, 'utf8') > LIMITS.detailsBytes) {
116
+ throw new SystemChangeInputError(`details が大きすぎます(JSON で ${LIMITS.detailsBytes} バイトまで)`);
117
+ }
118
+ // JSON にできる値だけを残す(undefined・関数などを落とす)
119
+ details = JSON.parse(json);
120
+ }
121
+ const record = {
122
+ kind: kind,
123
+ result: result,
124
+ target,
125
+ occurredAt,
126
+ };
127
+ const version = optional('version');
128
+ const commit = optional('commit');
129
+ const actor = optional('actor');
130
+ if (version)
131
+ record.version = version;
132
+ if (commit)
133
+ record.commit = commit;
134
+ if (actor)
135
+ record.actor = actor;
136
+ if (details && Object.keys(details).length > 0)
137
+ record.details = details;
138
+ return changeId ? { record, changeId } : { record };
139
+ }
140
+ /** 終わった状態(succeeded / failed) */
141
+ export const isFinished = (r) => r !== 'started';
142
+ export function decideRunWrite(existing, record) {
143
+ if (!existing) {
144
+ const set = { ...record };
145
+ if (record.result === 'started')
146
+ set.startedAt = record.occurredAt;
147
+ else
148
+ set.finishedAt = record.occurredAt;
149
+ return { action: 'create', set };
150
+ }
151
+ if (isFinished(existing.result)) {
152
+ if (record.result === 'started')
153
+ return { action: 'ignore', reason: 'stale-start' };
154
+ if (record.result === existing.result)
155
+ return { action: 'ignore', reason: 'duplicate-finish' };
156
+ return { action: 'ignore', reason: 'conflict' };
157
+ }
158
+ if (record.result === 'started')
159
+ return { action: 'ignore', reason: 'duplicate-start' };
160
+ // 終了の時刻は、入力の時刻(無ければ、現在。toSystemChangeRecord が occurredAt に入れている)
161
+ const finishedAt = record.occurredAt;
162
+ const set = { result: record.result, finishedAt };
163
+ const startedAt = typeof existing.startedAt === 'string' ? existing.startedAt : undefined;
164
+ if (startedAt) {
165
+ const d = Date.parse(finishedAt) - Date.parse(startedAt);
166
+ // 時計のずれ・時刻の指定の誤りで負になったときは、所要時間を付けない
167
+ if (Number.isFinite(d) && d >= 0)
168
+ set.durationMs = d;
169
+ }
170
+ // 入力にある項目だけを $set する(details は Records が深くマージする。開始のキーは残る。
171
+ // ただし配列〔lambdaReleases など〕は、マージせず、入力の値で置き換わる。DynamoDB Local の統合テストで確認)
172
+ for (const k of ['target', 'version', 'commit', 'actor', 'details']) {
173
+ if (record[k] !== undefined)
174
+ set[k] = record[k];
175
+ }
176
+ const merged = {
177
+ kind: existing.kind,
178
+ result: record.result,
179
+ target: (set.target ?? existing.target),
180
+ occurredAt: existing.occurredAt,
181
+ finishedAt,
182
+ };
183
+ for (const k of ['version', 'commit', 'actor', 'startedAt']) {
184
+ const v = set[k] ?? existing[k];
185
+ if (typeof v === 'string')
186
+ merged[k] = v;
187
+ }
188
+ if (set.durationMs !== undefined)
189
+ merged.durationMs = set.durationMs;
190
+ const details = {
191
+ ...(isPlainObject(existing.details) ? existing.details : {}),
192
+ ...(record.details ?? {}),
193
+ };
194
+ if (Object.keys(details).length > 0)
195
+ merged.details = details;
196
+ return { action: 'finish', set, merged };
197
+ }
198
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/system-changes/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * システム変更履歴(systemChanges)の書き込みと Slack 通知(ADR 0006 の (a))。
3
+ *
4
+ * asanowa(ADR-0044)と astro(ADR-0011)に複製されていたロジックを、1 つにした。アプリ固有の差は注入する:
5
+ * - アプリ名(ログの `app`、Slack の見出し、失敗の案内の Records Lambda 名): `options.app`
6
+ * - 書き込み先: `SystemChangesCollection`(生のコレクションは `systemChangesCollectionOf` で包む。権限チェック層を
7
+ * 通すアプリは、自分の層で包んだものを渡す)
8
+ * - ログ: `options.log`(既定は notify の `logJson`)
9
+ * - Slack: `options.slack`(webhook の取得、環境名、管理画面の URL)
10
+ *
11
+ * テーブルには、Records(DynamoClient)経由でしか触れない(docs/DYNAMODB_ACCESS.md)。
12
+ */
13
+ import type { RecordsCollection } from '../client/recordsClient.js';
14
+ import { type StoredSystemChange, type SystemChangeRecord } from './record.js';
15
+ /** 記録の HTTP の時間切れ(ミリ秒)。Lambda の時間切れ(両アプリの Terraform で 15 秒)より短くする */
16
+ export declare const RECORD_HTTP_TIMEOUT_MS = 5000;
17
+ /** Slack への送信の時間切れ(ミリ秒) */
18
+ export declare const SLACK_HTTP_TIMEOUT_MS = 3000;
19
+ /**
20
+ * 書き込み先(テストでは差し替える)。
21
+ * findOne・updateOne は changeId の記録だけが使う(無いと changeId の入力は書けない。insertOne だけの
22
+ * 差し替えも、そのまま使えるように任意にしている)
23
+ */
24
+ export interface SystemChangesCollection {
25
+ /** changeId の無い記録: 新しい 1 件(id は Records が ULID で付ける) */
26
+ insertOne(document: SystemChangeRecord): Promise<{
27
+ insertedId: string;
28
+ }>;
29
+ /** id の 1 件。無ければ null */
30
+ findOne?(id: string): Promise<StoredSystemChange | null>;
31
+ /** id のレコードに $set する。upsert: true なら、無ければ作る(createdAt / updatedAt は Records が付ける) */
32
+ updateOne?(id: string, set: Partial<SystemChangeRecord>, options: {
33
+ upsert: boolean;
34
+ }): Promise<void>;
35
+ }
36
+ export type SystemChangeLogLevel = 'info' | 'warn' | 'error';
37
+ /** 構造化ログの出力先。fields には app・target(systemChanges)が入る */
38
+ export type SystemChangeLog = (level: SystemChangeLogLevel, fields: Record<string, unknown>) => void;
39
+ export interface RecordSystemChangeOptions {
40
+ /** アプリ名(ログの app、Slack の見出し、失敗の案内の `<app>-<env>-records`) */
41
+ app: string;
42
+ /** 現在時刻(テスト用) */
43
+ now?: () => Date;
44
+ /** ログの出力先。既定は notify の logJson(JSON 1 行。Lambda の JSON 形式ではオブジェクト) */
45
+ log?: SystemChangeLog;
46
+ }
47
+ /** recordSystemChange の結果。ignored は、changeId の記録で、状態を変えずに無視したとき(書いていない) */
48
+ export interface RecordOutcome {
49
+ id: string;
50
+ occurredAt: string;
51
+ /** Slack に出す内容(changeId の更新では、既存と入力を合わせたもの) */
52
+ record: SystemChangeRecord;
53
+ ignored?: true;
54
+ }
55
+ /**
56
+ * 検証して書く。changeId が無ければ 1 件を insertOne、あれば、その ID のレコードを作成・更新する(decideRunWrite)。
57
+ * 失敗は構造化ログに出して、例外にする。
58
+ * 読み取り → 書き込みは非アトミック(Records に条件付き書き込みが無い)。同じ changeId の呼び出しが、同時に
59
+ * 走らない前提(ラッパーは、開始と終了を順に呼ぶ)
60
+ */
61
+ export declare function recordSystemChange(collection: SystemChangesCollection, input: unknown, options: RecordSystemChangeOptions): Promise<RecordOutcome>;
62
+ /**
63
+ * Records のコレクション(DynamoClient)を、書き込み先にする(権限チェック層を持たないアプリの既定)。
64
+ * 1 件の読み取りは `find({ id }, { sort: { id: 'asc' }, limit: 1 })`。sort を id にすると、Records の ID の経路
65
+ * (PK/SK の Query 1 回。executeSpecificIdQuery)に入る(sort が無いと updatedAt の既定になり、全件の走査になる)。
66
+ * 無い ID は空配列で null になる(`findOne` は 404〔ITEM_NOT_FOUND〕を
67
+ * 返し、Records が warn のログを出して運用通知に届くため、使わない。astro で確認した動作)
68
+ */
69
+ export declare function systemChangesCollectionOf(raw: RecordsCollection<StoredSystemChange>): Required<SystemChangesCollection>;
70
+ /** Slack 通知の設定。webhookParam が無ければ、送らない */
71
+ export interface SystemChangeSlackOptions {
72
+ /** webhook の URL の置き場所(SSM のパラメータ名など)。getWebhookUrl に渡す */
73
+ webhookParam?: string;
74
+ /** webhook の URL を取る(秘密。キャッシュしてよい) */
75
+ getWebhookUrl: (param: string) => Promise<string>;
76
+ /** 見出しの `[app/env]` の env */
77
+ env?: string;
78
+ /** 管理画面の URL(あれば、詳細 `/systemChanges/{id}/show` へのリンクを付ける) */
79
+ adminBaseUrl?: string;
80
+ /** 開始(started)も送る(既定 false) */
81
+ notifyStarted?: boolean;
82
+ /** Webhook の URL が、この文字列で始まること(既定 https://hooks.slack.com/。notify の createSlackChannel と同じ) */
83
+ urlPrefix?: string;
84
+ fetchImpl?: typeof fetch;
85
+ /** 既定 SLACK_HTTP_TIMEOUT_MS(3 秒) */
86
+ timeoutMs?: number;
87
+ }
88
+ export interface RecordAndNotifyOptions extends RecordSystemChangeOptions {
89
+ slack?: SystemChangeSlackOptions;
90
+ }
91
+ /** 記録 Lambda の応答 */
92
+ export interface SystemChangeResponse {
93
+ id: string;
94
+ occurredAt: string;
95
+ /** Slack に送ったか(送らない指定・開始・設定なし・無視・送信の失敗は false) */
96
+ notified: boolean;
97
+ /** changeId の記録で、状態を変えずに無視したときだけ付ける(そのときは、Slack に送らない) */
98
+ ignored?: true;
99
+ }
100
+ /**
101
+ * 記録して、結果を Slack に送る。送信の失敗は、記録を失敗にしない(構造化ログ warn に出す。URL は出さない)
102
+ */
103
+ export declare function recordAndNotify(collection: SystemChangesCollection, input: unknown, options: RecordAndNotifyOptions): Promise<SystemChangeResponse>;
104
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/system-changes/recorder.ts"],"names":[],"mappings":"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"}