@empire-builder-kit/nx 1.0.0-rc.44 → 1.0.0-rc.46
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/package.json +3 -3
- package/src/blueprints/external-sandbox.d.ts +1 -1
- package/src/cli/main.d.ts +11 -5
- package/src/cli/main.js +88 -29
- package/src/container-images.d.ts +1 -1
- package/src/container-images.js +1 -1
- package/src/developer-agent/generator-planning.js +10 -3
- package/src/developer-agent/operation-registry.js +1 -1
- package/src/executors/deployment-plan/deployment-plan.js +5 -1
- package/src/executors/deployment-plan/schema.json +1 -1
- package/src/executors/fleet/fleet.js +4 -0
- package/src/executors/fleet/schema.json +1 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +3 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +28 -0
- package/src/executors/github-bootstrap/schema.json +14 -0
- package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
- package/src/executors/local-deploy/federated-credential-transport.js +15 -5
- package/src/foundation/cost-estimate.d.ts +8 -3
- package/src/foundation/cost-estimate.js +42 -19
- package/src/foundation/index.d.ts +1 -0
- package/src/foundation/index.js +1 -0
- package/src/foundation/local-private-smoke-access.js +6 -0
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +8 -1
- package/src/foundation/observability.d.ts +42 -0
- package/src/foundation/observability.js +181 -0
- package/src/foundation/preflight.js +1 -1
- package/src/generators/function/function.js +5 -2
- package/src/generators/observability-infrastructure.d.ts +2 -0
- package/src/generators/observability-infrastructure.js +31 -0
- package/src/generators/observability-policy.d.ts +4 -0
- package/src/generators/observability-policy.js +23 -0
- package/src/generators/preset/dependency-overrides.d.ts +17 -6
- package/src/generators/preset/dependency-overrides.js +17 -6
- package/src/generators/preset/tool-versions.d.ts +2 -2
- package/src/generators/preset/tool-versions.js +2 -2
- package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
- package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
- package/src/generators/preset/workspace-files-agent-development.js +42 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +17 -4
- package/src/generators/preset/workspace-files-thin-docs.js +10 -22
- package/src/generators/preset/workspace-files.d.ts +1 -0
- package/src/generators/preset/workspace-files.js +120 -35
- package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
- package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
- package/src/generators/slice/built-in-plan-data-files.js +10 -3
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
- package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
- package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
- package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
- package/src/generators/slice/built-in-plan-metadata-files.js +37 -2
- package/src/generators/slice/built-in-plan-operations-files.js +106 -6
- package/src/generators/slice/built-in-plan-project-files.js +1 -0
- package/src/generators/slice/built-in-plan-release-files.js +2 -44
- package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
- package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
- package/src/generators/slice/built-in-plan-shared.js +29 -2
- package/src/generators/slice/built-in-plan-verification-files.js +0 -1
- package/src/generators/slice/built-in-plan-web-files.js +22 -6
- package/src/generators/slice/built-in-plan.js +2 -2
- package/src/generators/slice/slice.js +2 -0
- package/src/generators/upgrade/upgrade.d.ts +4 -3
- package/src/generators/upgrade/upgrade.js +139 -29
- package/src/generators/workload/frozen-container.d.ts +1 -1
- package/src/generators/workload/reliability-registration.d.ts +1 -1
- package/src/generators/workload/reliability-registration.js +9 -9
- package/src/generators/workload/workload.d.ts +1 -1
- package/src/generators/workload/workload.js +32 -10
- package/src/git-policy/github-bootstrap.d.ts +18 -0
- package/src/git-policy/github-bootstrap.js +99 -6
- package/src/local-dev/dev-command-core.d.ts +24 -3
- package/src/local-dev/dev-command-core.js +124 -12
- package/src/local-dev/errors.d.ts +19 -1
- package/src/local-dev/errors.js +18 -1
- package/src/local-dev/fleet-client.d.ts +64 -6
- package/src/local-dev/fleet-client.js +189 -15
- package/src/local-dev/fleet-process.d.ts +47 -2
- package/src/local-dev/fleet-process.js +248 -31
- package/src/local-dev/fleet-runtime.d.ts +37 -0
- package/src/local-dev/fleet-runtime.js +127 -69
- package/src/local-dev/fleet-session.d.ts +1 -0
- package/src/local-dev/fleet-session.js +83 -7
- package/src/local-dev/fleet-state.d.ts +5 -0
- package/src/local-dev/fleet-state.js +11 -2
- package/src/local-dev/fleet-supervisor-daemon.js +1 -5
- package/src/local-dev/fleet-supervisor.d.ts +7 -0
- package/src/local-dev/fleet-supervisor.js +83 -12
- package/src/local-dev/slice-runner.d.ts +19 -1
- package/src/local-dev/slice-runner.js +290 -91
- package/src/repository-policy/deployment-plan.d.ts +1 -1
- package/src/repository-policy/deployment-plan.js +16 -1
- package/src/secrets/generated-aws-control-plane.d.ts +11 -1
- package/src/secrets/generated-aws-control-plane.js +92 -29
- package/src/secrets/operator.d.ts +3 -2
- package/src/secrets/operator.js +27 -5
- package/src/supply-chain/blueprint-verification-authority.js +24 -3
- package/src/supply-chain/generated-files.js +9 -0
- package/src/supply-chain/sbom.js +74 -12
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one identity table for every alarm a Standard Slice registers.
|
|
3
|
+
*
|
|
4
|
+
* The seeded operations/reliability.json (its alarmRegistrations and
|
|
5
|
+
* deployedAlarmResources) and every managed MetricAlarm description
|
|
6
|
+
* (infra/reliability.ts, infra/product.ts, infra/key-value-retention.ts) are
|
|
7
|
+
* rendered from this table. The generated `scripts/verify-reliability.mjs
|
|
8
|
+
* --deployed` compares the inventory's owner, route and runbook with each
|
|
9
|
+
* deployed alarm's description, so a value written in two places can drift
|
|
10
|
+
* into a deployed alarm the verifier rejects (CROSS-SLICE-RETENTION-IDENTITY-003).
|
|
11
|
+
*
|
|
12
|
+
* Where the seeded and managed values disagreed, the seeded value is kept:
|
|
13
|
+
* an upgrade rewrites only the managed emitters, so aligning them with the
|
|
14
|
+
* seeded inventory repairs every existing adopter without a hand edit.
|
|
15
|
+
*
|
|
16
|
+
* For the same reason the runbook of an existing ID, and the owner and route
|
|
17
|
+
* from sliceAlarmOwnership, are frozen for existing adopters:
|
|
18
|
+
* built-in-plan-alarm-identity.spec.ts pins them, and changing one needs a
|
|
19
|
+
* seeded operations/reliability.json migration.
|
|
20
|
+
*/
|
|
21
|
+
declare const SLICE_ALARM_IDENTITIES: {
|
|
22
|
+
readonly 'alert-delivery-degraded': {
|
|
23
|
+
readonly businessSymptom: "The primary alert route is failing delivery.";
|
|
24
|
+
readonly runbook: "alert-delivery";
|
|
25
|
+
};
|
|
26
|
+
readonly 'api-fast-burn': {
|
|
27
|
+
readonly businessSymptom: "Critical API operations are failing.";
|
|
28
|
+
readonly runbook: "api-recovery";
|
|
29
|
+
};
|
|
30
|
+
readonly 'api-slow-burn': {
|
|
31
|
+
readonly businessSymptom: "Critical API operations have sustained failures.";
|
|
32
|
+
readonly runbook: "api-recovery";
|
|
33
|
+
};
|
|
34
|
+
readonly 'async-target-dlq-depth': {
|
|
35
|
+
readonly businessSymptom: "An asynchronous target has terminal failures.";
|
|
36
|
+
readonly runbook: "async-recovery";
|
|
37
|
+
};
|
|
38
|
+
readonly 'data-export-cleanup-deadline': {
|
|
39
|
+
readonly businessSymptom: "Confidential-export residue exceeded its deletion deadline.";
|
|
40
|
+
readonly runbook: "database-recovery";
|
|
41
|
+
};
|
|
42
|
+
readonly 'data-export-cleanup-failure': {
|
|
43
|
+
readonly businessSymptom: "The confidential-export cleanup invocation is failing.";
|
|
44
|
+
readonly runbook: "database-recovery";
|
|
45
|
+
};
|
|
46
|
+
readonly 'data-export-cleanup-invocation-missing': {
|
|
47
|
+
readonly businessSymptom: "The scheduled confidential-export cleanup is not running.";
|
|
48
|
+
readonly runbook: "database-recovery";
|
|
49
|
+
};
|
|
50
|
+
readonly 'data-protection-findings': {
|
|
51
|
+
readonly businessSymptom: "A managed log group contains a protected-data finding.";
|
|
52
|
+
readonly runbook: "log-investigation";
|
|
53
|
+
};
|
|
54
|
+
readonly 'database-capacity': {
|
|
55
|
+
readonly businessSymptom: "Slice data access or durable event delivery is being throttled.";
|
|
56
|
+
readonly runbook: "database-recovery";
|
|
57
|
+
};
|
|
58
|
+
readonly 'event-delivery-attempts': {
|
|
59
|
+
readonly businessSymptom: "A domain-event intent is repeatedly failing delivery.";
|
|
60
|
+
readonly runbook: "async-recovery";
|
|
61
|
+
};
|
|
62
|
+
readonly 'event-dlq-depth': {
|
|
63
|
+
readonly businessSymptom: "Domain-event processing has terminal failures.";
|
|
64
|
+
readonly runbook: "async-recovery";
|
|
65
|
+
};
|
|
66
|
+
readonly 'event-intent-rejected': {
|
|
67
|
+
readonly businessSymptom: "A retained domain-event intent cannot be delivered.";
|
|
68
|
+
readonly runbook: "async-recovery";
|
|
69
|
+
};
|
|
70
|
+
readonly 'function-failure': {
|
|
71
|
+
readonly businessSymptom: "The generated confidential-export cleanup Function has a terminal invocation error.";
|
|
72
|
+
readonly runbook: "api-recovery";
|
|
73
|
+
};
|
|
74
|
+
readonly 'inbox-cleanup-backlog': {
|
|
75
|
+
readonly businessSymptom: "Terminal asynchronous inbox records are accumulating.";
|
|
76
|
+
readonly runbook: "async-recovery";
|
|
77
|
+
};
|
|
78
|
+
readonly 'inbox-cleanup-errors': {
|
|
79
|
+
readonly businessSymptom: "The asynchronous inbox cleanup job is failing.";
|
|
80
|
+
readonly runbook: "async-recovery";
|
|
81
|
+
};
|
|
82
|
+
readonly 'key-value-reconciliation-errors': {
|
|
83
|
+
readonly businessSymptom: "Durable domain-event recovery is failing.";
|
|
84
|
+
readonly runbook: "async-recovery";
|
|
85
|
+
};
|
|
86
|
+
readonly 'key-value-stream-delivery-errors': {
|
|
87
|
+
readonly businessSymptom: "The key-value stream delivery Function failed an invocation.";
|
|
88
|
+
readonly runbook: "async-recovery";
|
|
89
|
+
};
|
|
90
|
+
readonly 'key-value-stream-iterator-age': {
|
|
91
|
+
readonly businessSymptom: "Prompt domain-event delivery from the key-value stream is falling behind.";
|
|
92
|
+
readonly runbook: "async-recovery";
|
|
93
|
+
};
|
|
94
|
+
readonly 'outbox-backlog': {
|
|
95
|
+
readonly businessSymptom: "Durable domain-event intents remain undelivered.";
|
|
96
|
+
readonly runbook: "async-recovery";
|
|
97
|
+
};
|
|
98
|
+
readonly 'outbox-relay-errors': {
|
|
99
|
+
readonly businessSymptom: "The isolated performance-sink outbox relay is failing.";
|
|
100
|
+
readonly runbook: "async-recovery";
|
|
101
|
+
};
|
|
102
|
+
readonly 'performance-load-cleanup-backlog': {
|
|
103
|
+
readonly businessSymptom: "Expired protected performance-load control records are accumulating.";
|
|
104
|
+
readonly runbook: "performance-capacity-cost";
|
|
105
|
+
};
|
|
106
|
+
readonly 'performance-load-cleanup-deadline': {
|
|
107
|
+
readonly businessSymptom: "Protected performance-load residue exceeded its cleanup deadline.";
|
|
108
|
+
readonly runbook: "performance-capacity-cost";
|
|
109
|
+
};
|
|
110
|
+
readonly 'provider-dlq-depth': {
|
|
111
|
+
readonly businessSymptom: "Provider deliveries have terminal failures.";
|
|
112
|
+
readonly runbook: "async-recovery";
|
|
113
|
+
};
|
|
114
|
+
readonly 'provider-queue-age': {
|
|
115
|
+
readonly businessSymptom: "Accepted provider work is missing its deadline.";
|
|
116
|
+
readonly runbook: "async-recovery";
|
|
117
|
+
};
|
|
118
|
+
readonly 'retention-job-failure': {
|
|
119
|
+
readonly businessSymptom: "The built-in retention Job terminated unsuccessfully.";
|
|
120
|
+
readonly runbook: "database-recovery";
|
|
121
|
+
};
|
|
122
|
+
readonly 'sql-outbox-attempts': {
|
|
123
|
+
readonly businessSymptom: "A relational event intent is repeatedly failing delivery.";
|
|
124
|
+
readonly runbook: "async-recovery";
|
|
125
|
+
};
|
|
126
|
+
readonly 'sql-outbox-backlog': {
|
|
127
|
+
readonly businessSymptom: "Relational event intents remain undelivered.";
|
|
128
|
+
readonly runbook: "async-recovery";
|
|
129
|
+
};
|
|
130
|
+
readonly 'sql-outbox-relay-errors': {
|
|
131
|
+
readonly businessSymptom: "Relational event delivery is failing.";
|
|
132
|
+
readonly runbook: "async-recovery";
|
|
133
|
+
};
|
|
134
|
+
readonly 'web-latency-fast-burn': {
|
|
135
|
+
readonly businessSymptom: "Server-rendered routes exceed the latency objective.";
|
|
136
|
+
readonly runbook: "web-recovery";
|
|
137
|
+
};
|
|
138
|
+
readonly 'web-latency-slow-burn': {
|
|
139
|
+
readonly businessSymptom: "Server-rendered routes have sustained latency degradation.";
|
|
140
|
+
readonly runbook: "web-recovery";
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
export type SliceAlarmId = keyof typeof SLICE_ALARM_IDENTITIES;
|
|
144
|
+
export interface SliceAlarmIdentity {
|
|
145
|
+
businessSymptom: string;
|
|
146
|
+
id: SliceAlarmId;
|
|
147
|
+
owner: string;
|
|
148
|
+
route: string;
|
|
149
|
+
/** The Slice-relative runbook path that the description and inventory name. */
|
|
150
|
+
runbook: string;
|
|
151
|
+
/** The alarm registration's reference to the same runbook. */
|
|
152
|
+
runbookRef: string;
|
|
153
|
+
}
|
|
154
|
+
/** The owner and escalation route every built-in Slice alarm carries. */
|
|
155
|
+
export declare function sliceAlarmOwnership(name: string): {
|
|
156
|
+
owner: string;
|
|
157
|
+
route: string;
|
|
158
|
+
};
|
|
159
|
+
export declare function sliceAlarmIdentity(name: string, id: SliceAlarmId): SliceAlarmIdentity;
|
|
160
|
+
/**
|
|
161
|
+
* The generated description helper's argument for one alarm, as TypeScript
|
|
162
|
+
* source. Both generated helpers (`alarmDescription` in infra/reliability.ts
|
|
163
|
+
* and `operationalAlarmDescription` in infra/product.ts) add the owner,
|
|
164
|
+
* route, schema version and Slice from sliceAlarmOwnership.
|
|
165
|
+
*/
|
|
166
|
+
export declare function renderSliceAlarmDescriptionInput(name: string, id: SliceAlarmId, indent: string): string;
|
|
167
|
+
/**
|
|
168
|
+
* The complete alarm description object for one alarm, as TypeScript source
|
|
169
|
+
* for an emitter without a description helper. Keys are in the helpers'
|
|
170
|
+
* order.
|
|
171
|
+
*/
|
|
172
|
+
export declare function renderSliceAlarmDescriptionObject(name: string, id: SliceAlarmId): string;
|
|
173
|
+
export {};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sliceAlarmOwnership = sliceAlarmOwnership;
|
|
4
|
+
exports.sliceAlarmIdentity = sliceAlarmIdentity;
|
|
5
|
+
exports.renderSliceAlarmDescriptionInput = renderSliceAlarmDescriptionInput;
|
|
6
|
+
exports.renderSliceAlarmDescriptionObject = renderSliceAlarmDescriptionObject;
|
|
7
|
+
/**
|
|
8
|
+
* The one identity table for every alarm a Standard Slice registers.
|
|
9
|
+
*
|
|
10
|
+
* The seeded operations/reliability.json (its alarmRegistrations and
|
|
11
|
+
* deployedAlarmResources) and every managed MetricAlarm description
|
|
12
|
+
* (infra/reliability.ts, infra/product.ts, infra/key-value-retention.ts) are
|
|
13
|
+
* rendered from this table. The generated `scripts/verify-reliability.mjs
|
|
14
|
+
* --deployed` compares the inventory's owner, route and runbook with each
|
|
15
|
+
* deployed alarm's description, so a value written in two places can drift
|
|
16
|
+
* into a deployed alarm the verifier rejects (CROSS-SLICE-RETENTION-IDENTITY-003).
|
|
17
|
+
*
|
|
18
|
+
* Where the seeded and managed values disagreed, the seeded value is kept:
|
|
19
|
+
* an upgrade rewrites only the managed emitters, so aligning them with the
|
|
20
|
+
* seeded inventory repairs every existing adopter without a hand edit.
|
|
21
|
+
*
|
|
22
|
+
* For the same reason the runbook of an existing ID, and the owner and route
|
|
23
|
+
* from sliceAlarmOwnership, are frozen for existing adopters:
|
|
24
|
+
* built-in-plan-alarm-identity.spec.ts pins them, and changing one needs a
|
|
25
|
+
* seeded operations/reliability.json migration.
|
|
26
|
+
*/
|
|
27
|
+
const SLICE_ALARM_IDENTITIES = {
|
|
28
|
+
'alert-delivery-degraded': {
|
|
29
|
+
businessSymptom: 'The primary alert route is failing delivery.',
|
|
30
|
+
runbook: 'alert-delivery',
|
|
31
|
+
},
|
|
32
|
+
'api-fast-burn': {
|
|
33
|
+
businessSymptom: 'Critical API operations are failing.',
|
|
34
|
+
runbook: 'api-recovery',
|
|
35
|
+
},
|
|
36
|
+
'api-slow-burn': {
|
|
37
|
+
businessSymptom: 'Critical API operations have sustained failures.',
|
|
38
|
+
runbook: 'api-recovery',
|
|
39
|
+
},
|
|
40
|
+
'async-target-dlq-depth': {
|
|
41
|
+
businessSymptom: 'An asynchronous target has terminal failures.',
|
|
42
|
+
runbook: 'async-recovery',
|
|
43
|
+
},
|
|
44
|
+
'data-export-cleanup-deadline': {
|
|
45
|
+
businessSymptom: 'Confidential-export residue exceeded its deletion deadline.',
|
|
46
|
+
runbook: 'database-recovery',
|
|
47
|
+
},
|
|
48
|
+
'data-export-cleanup-failure': {
|
|
49
|
+
businessSymptom: 'The confidential-export cleanup invocation is failing.',
|
|
50
|
+
runbook: 'database-recovery',
|
|
51
|
+
},
|
|
52
|
+
'data-export-cleanup-invocation-missing': {
|
|
53
|
+
businessSymptom: 'The scheduled confidential-export cleanup is not running.',
|
|
54
|
+
runbook: 'database-recovery',
|
|
55
|
+
},
|
|
56
|
+
'data-protection-findings': {
|
|
57
|
+
businessSymptom: 'A managed log group contains a protected-data finding.',
|
|
58
|
+
runbook: 'log-investigation',
|
|
59
|
+
},
|
|
60
|
+
'database-capacity': {
|
|
61
|
+
businessSymptom: 'Slice data access or durable event delivery is being throttled.',
|
|
62
|
+
runbook: 'database-recovery',
|
|
63
|
+
},
|
|
64
|
+
'event-delivery-attempts': {
|
|
65
|
+
businessSymptom: 'A domain-event intent is repeatedly failing delivery.',
|
|
66
|
+
runbook: 'async-recovery',
|
|
67
|
+
},
|
|
68
|
+
'event-dlq-depth': {
|
|
69
|
+
businessSymptom: 'Domain-event processing has terminal failures.',
|
|
70
|
+
runbook: 'async-recovery',
|
|
71
|
+
},
|
|
72
|
+
'event-intent-rejected': {
|
|
73
|
+
businessSymptom: 'A retained domain-event intent cannot be delivered.',
|
|
74
|
+
runbook: 'async-recovery',
|
|
75
|
+
},
|
|
76
|
+
'function-failure': {
|
|
77
|
+
businessSymptom: 'The generated confidential-export cleanup Function has a terminal invocation error.',
|
|
78
|
+
runbook: 'api-recovery',
|
|
79
|
+
},
|
|
80
|
+
'inbox-cleanup-backlog': {
|
|
81
|
+
businessSymptom: 'Terminal asynchronous inbox records are accumulating.',
|
|
82
|
+
runbook: 'async-recovery',
|
|
83
|
+
},
|
|
84
|
+
'inbox-cleanup-errors': {
|
|
85
|
+
businessSymptom: 'The asynchronous inbox cleanup job is failing.',
|
|
86
|
+
runbook: 'async-recovery',
|
|
87
|
+
},
|
|
88
|
+
'key-value-reconciliation-errors': {
|
|
89
|
+
businessSymptom: 'Durable domain-event recovery is failing.',
|
|
90
|
+
runbook: 'async-recovery',
|
|
91
|
+
},
|
|
92
|
+
'key-value-stream-delivery-errors': {
|
|
93
|
+
businessSymptom: 'The key-value stream delivery Function failed an invocation.',
|
|
94
|
+
runbook: 'async-recovery',
|
|
95
|
+
},
|
|
96
|
+
'key-value-stream-iterator-age': {
|
|
97
|
+
businessSymptom: 'Prompt domain-event delivery from the key-value stream is falling behind.',
|
|
98
|
+
runbook: 'async-recovery',
|
|
99
|
+
},
|
|
100
|
+
'outbox-backlog': {
|
|
101
|
+
businessSymptom: 'Durable domain-event intents remain undelivered.',
|
|
102
|
+
runbook: 'async-recovery',
|
|
103
|
+
},
|
|
104
|
+
'outbox-relay-errors': {
|
|
105
|
+
businessSymptom: 'The isolated performance-sink outbox relay is failing.',
|
|
106
|
+
runbook: 'async-recovery',
|
|
107
|
+
},
|
|
108
|
+
'performance-load-cleanup-backlog': {
|
|
109
|
+
businessSymptom: 'Expired protected performance-load control records are accumulating.',
|
|
110
|
+
runbook: 'performance-capacity-cost',
|
|
111
|
+
},
|
|
112
|
+
'performance-load-cleanup-deadline': {
|
|
113
|
+
businessSymptom: 'Protected performance-load residue exceeded its cleanup deadline.',
|
|
114
|
+
runbook: 'performance-capacity-cost',
|
|
115
|
+
},
|
|
116
|
+
'provider-dlq-depth': {
|
|
117
|
+
businessSymptom: 'Provider deliveries have terminal failures.',
|
|
118
|
+
runbook: 'async-recovery',
|
|
119
|
+
},
|
|
120
|
+
'provider-queue-age': {
|
|
121
|
+
businessSymptom: 'Accepted provider work is missing its deadline.',
|
|
122
|
+
runbook: 'async-recovery',
|
|
123
|
+
},
|
|
124
|
+
// The Step Functions retention workflow is a database-lifecycle job: its
|
|
125
|
+
// recovery belongs with the other lifecycle alarms in database-recovery.md,
|
|
126
|
+
// which routes the operator to the Job's own README for the run procedure.
|
|
127
|
+
'retention-job-failure': {
|
|
128
|
+
businessSymptom: 'The built-in retention Job terminated unsuccessfully.',
|
|
129
|
+
runbook: 'database-recovery',
|
|
130
|
+
},
|
|
131
|
+
'sql-outbox-attempts': {
|
|
132
|
+
businessSymptom: 'A relational event intent is repeatedly failing delivery.',
|
|
133
|
+
runbook: 'async-recovery',
|
|
134
|
+
},
|
|
135
|
+
'sql-outbox-backlog': {
|
|
136
|
+
businessSymptom: 'Relational event intents remain undelivered.',
|
|
137
|
+
runbook: 'async-recovery',
|
|
138
|
+
},
|
|
139
|
+
'sql-outbox-relay-errors': {
|
|
140
|
+
businessSymptom: 'Relational event delivery is failing.',
|
|
141
|
+
runbook: 'async-recovery',
|
|
142
|
+
},
|
|
143
|
+
'web-latency-fast-burn': {
|
|
144
|
+
businessSymptom: 'Server-rendered routes exceed the latency objective.',
|
|
145
|
+
runbook: 'web-recovery',
|
|
146
|
+
},
|
|
147
|
+
'web-latency-slow-burn': {
|
|
148
|
+
businessSymptom: 'Server-rendered routes have sustained latency degradation.',
|
|
149
|
+
runbook: 'web-recovery',
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
/** The owner and escalation route every built-in Slice alarm carries. */
|
|
153
|
+
function sliceAlarmOwnership(name) {
|
|
154
|
+
return { owner: `${name}-product`, route: `${name}-on-call` };
|
|
155
|
+
}
|
|
156
|
+
function sliceAlarmIdentity(name, id) {
|
|
157
|
+
const { businessSymptom, runbook } = SLICE_ALARM_IDENTITIES[id];
|
|
158
|
+
return {
|
|
159
|
+
businessSymptom,
|
|
160
|
+
id,
|
|
161
|
+
...sliceAlarmOwnership(name),
|
|
162
|
+
runbook: `docs/runbooks/${runbook}.md`,
|
|
163
|
+
runbookRef: `runbook:${runbook}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** A TypeScript single-quoted string literal. */
|
|
167
|
+
function typescriptString(value) {
|
|
168
|
+
if (/[\\'\n\r\u2028\u2029]/u.test(value)) {
|
|
169
|
+
throw new Error(`Alarm identity text cannot be rendered: ${value}`);
|
|
170
|
+
}
|
|
171
|
+
return `'${value}'`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The generated description helper's argument for one alarm, as TypeScript
|
|
175
|
+
* source. Both generated helpers (`alarmDescription` in infra/reliability.ts
|
|
176
|
+
* and `operationalAlarmDescription` in infra/product.ts) add the owner,
|
|
177
|
+
* route, schema version and Slice from sliceAlarmOwnership.
|
|
178
|
+
*/
|
|
179
|
+
function renderSliceAlarmDescriptionInput(name, id, indent) {
|
|
180
|
+
const identity = sliceAlarmIdentity(name, id);
|
|
181
|
+
const symptom = `${indent} businessSymptom: ${typescriptString(identity.businessSymptom)},`;
|
|
182
|
+
return [
|
|
183
|
+
'{',
|
|
184
|
+
// Wrap a symptom that would not fit 80 columns, as the template did.
|
|
185
|
+
symptom.length <= 80
|
|
186
|
+
? symptom
|
|
187
|
+
: `${indent} businessSymptom:\n${indent} ${typescriptString(identity.businessSymptom)},`,
|
|
188
|
+
`${indent} id: ${typescriptString(identity.id)},`,
|
|
189
|
+
`${indent} runbook: ${typescriptString(identity.runbook)},`,
|
|
190
|
+
`${indent}}`,
|
|
191
|
+
].join('\n');
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The complete alarm description object for one alarm, as TypeScript source
|
|
195
|
+
* for an emitter without a description helper. Keys are in the helpers'
|
|
196
|
+
* order.
|
|
197
|
+
*/
|
|
198
|
+
function renderSliceAlarmDescriptionObject(name, id) {
|
|
199
|
+
const identity = sliceAlarmIdentity(name, id);
|
|
200
|
+
return `{ businessSymptom: ${typescriptString(identity.businessSymptom)}, id: ${typescriptString(identity.id)}, owner: ${typescriptString(identity.owner)}, route: ${typescriptString(identity.route)}, runbook: ${typescriptString(identity.runbook)}, schemaVersion: 1, slice: ${typescriptString(name)} }`;
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=built-in-plan-alarm-identity.js.map
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.renderProductApiContractFiles = renderProductApiContractFiles;
|
|
4
4
|
const api_contract_1 = require("@empire-builder-kit/runtime/api-contract");
|
|
5
5
|
const index_js_1 = require("../../api-contract/index.js");
|
|
6
|
+
const built_in_plan_event_contract_literal_js_1 = require("./built-in-plan-event-contract-literal.js");
|
|
6
7
|
const built_in_plan_shared_js_1 = require("./built-in-plan-shared.js");
|
|
7
8
|
const local_source_identity_js_1 = require("./local-source-identity.js");
|
|
8
9
|
// Shared by the machine-readable contract and the generated operation source.
|
|
@@ -2040,6 +2041,7 @@ database code, or other implementation paths.
|
|
|
2040
2041
|
} from 'node:fs';
|
|
2041
2042
|
import { createRequire } from 'node:module';
|
|
2042
2043
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
2044
|
+
import ts from 'typescript';
|
|
2043
2045
|
|
|
2044
2046
|
const require = createRequire(import.meta.url);
|
|
2045
2047
|
const {
|
|
@@ -2211,6 +2213,139 @@ function validateContractExports(root, packageJson, publications) {
|
|
|
2211
2213
|
}
|
|
2212
2214
|
}
|
|
2213
2215
|
|
|
2216
|
+
${built_in_plan_event_contract_literal_js_1.EVENT_CONTRACT_LITERAL_SOURCE}
|
|
2217
|
+
function producerEventContract(producer, slice) {
|
|
2218
|
+
const path = join(producer.root, 'contracts', 'events.ts');
|
|
2219
|
+
if (!existsSync(path)) {
|
|
2220
|
+
throw new Error(
|
|
2221
|
+
'Producer ' + slice + ' publishes no contracts/events.ts EventContractSchema.',
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
const file = ts.createSourceFile(
|
|
2225
|
+
path,
|
|
2226
|
+
readFileSync(path, 'utf8'),
|
|
2227
|
+
ts.ScriptTarget.ESNext,
|
|
2228
|
+
true,
|
|
2229
|
+
ts.ScriptKind.TS,
|
|
2230
|
+
);
|
|
2231
|
+
let schema;
|
|
2232
|
+
for (const statement of file.statements) {
|
|
2233
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
2234
|
+
for (const variable of statement.declarationList.declarations) {
|
|
2235
|
+
if (
|
|
2236
|
+
ts.isIdentifier(variable.name) &&
|
|
2237
|
+
variable.name.text === 'EventContractSchema' &&
|
|
2238
|
+
variable.initializer
|
|
2239
|
+
) {
|
|
2240
|
+
if (schema !== undefined) {
|
|
2241
|
+
throw new Error(
|
|
2242
|
+
'Producer ' + slice + ' EventContractSchema export is ambiguous.',
|
|
2243
|
+
);
|
|
2244
|
+
}
|
|
2245
|
+
schema = eventLiteral(variable.initializer);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
if (
|
|
2250
|
+
schema?.kind !== 'ebk-event-contract-schema' ||
|
|
2251
|
+
schema.schemaVersion !== 1 ||
|
|
2252
|
+
schema.slice !== slice ||
|
|
2253
|
+
!Array.isArray(schema.events)
|
|
2254
|
+
) {
|
|
2255
|
+
throw new Error(
|
|
2256
|
+
'Producer ' +
|
|
2257
|
+
slice +
|
|
2258
|
+
' contracts/events.ts must export one canonical v1 EventContractSchema.',
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
return schema;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
// Bind each declared cross-Slice subscription to the producer's published
|
|
2265
|
+
// EventContractSchema, so a removed, renamed or re-versioned event fails here
|
|
2266
|
+
// instead of silently stopping delivery.
|
|
2267
|
+
async function checkEventSubscriptions(dependencies, projects) {
|
|
2268
|
+
const subscriptionPath = join(
|
|
2269
|
+
projectRoot,
|
|
2270
|
+
'contracts',
|
|
2271
|
+
'event-subscriptions.json',
|
|
2272
|
+
);
|
|
2273
|
+
if (!existsSync(subscriptionPath)) {
|
|
2274
|
+
throw new Error(
|
|
2275
|
+
'contracts/event-subscriptions.json is missing. Restore the seeded ' +
|
|
2276
|
+
'declaration with schemaVersion 1 and an empty subscriptions list.',
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
// The managed infrastructure validator is the one authority for the
|
|
2280
|
+
// declaration shape; this check runs it exactly as deployment does.
|
|
2281
|
+
const validator = await import(
|
|
2282
|
+
'data:text/javascript;base64,' +
|
|
2283
|
+
Buffer.from(
|
|
2284
|
+
ts.transpileModule(
|
|
2285
|
+
readFileSync(
|
|
2286
|
+
join(projectRoot, 'infra', 'event-subscriptions.ts'),
|
|
2287
|
+
'utf8',
|
|
2288
|
+
),
|
|
2289
|
+
{
|
|
2290
|
+
compilerOptions: {
|
|
2291
|
+
module: ts.ModuleKind.ESNext,
|
|
2292
|
+
target: ts.ScriptTarget.ES2022,
|
|
2293
|
+
},
|
|
2294
|
+
},
|
|
2295
|
+
).outputText,
|
|
2296
|
+
).toString('base64')
|
|
2297
|
+
);
|
|
2298
|
+
const subscriptions = validator.crossSliceEventSubscriptions(
|
|
2299
|
+
readJson(subscriptionPath),
|
|
2300
|
+
dependencies,
|
|
2301
|
+
);
|
|
2302
|
+
for (const subscription of subscriptions) {
|
|
2303
|
+
const producer = projects.get(subscription.source);
|
|
2304
|
+
if (
|
|
2305
|
+
!producer ||
|
|
2306
|
+
!dependencies.consumes.includes(producer.packageJson.name + '/contracts')
|
|
2307
|
+
) {
|
|
2308
|
+
throw new Error(
|
|
2309
|
+
'Subscribed source ' +
|
|
2310
|
+
subscription.source +
|
|
2311
|
+
' is not a consumed Product Slice contracts package.',
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2314
|
+
const schema = producerEventContract(producer, subscription.source);
|
|
2315
|
+
for (const detailType of subscription.detailTypes) {
|
|
2316
|
+
const published = schema.events.filter(
|
|
2317
|
+
(event) => event?.detailType === detailType,
|
|
2318
|
+
);
|
|
2319
|
+
if (
|
|
2320
|
+
published.length !== 1 ||
|
|
2321
|
+
published[0].source !== subscription.source
|
|
2322
|
+
) {
|
|
2323
|
+
throw new Error(
|
|
2324
|
+
'Producer ' +
|
|
2325
|
+
subscription.source +
|
|
2326
|
+
' does not publish ' +
|
|
2327
|
+
detailType +
|
|
2328
|
+
' in its EventContractSchema. Restore the producer event or ' +
|
|
2329
|
+
'remove the subscription from contracts/event-subscriptions.json.',
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
if (!subscription.schemaVersions.includes(published[0].schemaVersion)) {
|
|
2333
|
+
throw new Error(
|
|
2334
|
+
'Producer ' +
|
|
2335
|
+
subscription.source +
|
|
2336
|
+
' publishes ' +
|
|
2337
|
+
detailType +
|
|
2338
|
+
' schemaVersion ' +
|
|
2339
|
+
published[0].schemaVersion +
|
|
2340
|
+
', which ${name} does not subscribe to. Accept that version ' +
|
|
2341
|
+
'in api/event-consumer.ts, then declare it in ' +
|
|
2342
|
+
'contracts/event-subscriptions.json.',
|
|
2343
|
+
);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2214
2349
|
const declaration = readJson(declarationPath);
|
|
2215
2350
|
if (
|
|
2216
2351
|
!declaration ||
|
|
@@ -2345,6 +2480,7 @@ for (const publication of resolution.contracts) {
|
|
|
2345
2480
|
const current = [...(ownership.contracts?.consumes ?? [])].sort();
|
|
2346
2481
|
const expected = [...normalized.contracts.consumes];
|
|
2347
2482
|
if (action === 'check') {
|
|
2483
|
+
await checkEventSubscriptions(declaration, projects);
|
|
2348
2484
|
if (JSON.stringify(current) !== JSON.stringify(expected)) {
|
|
2349
2485
|
throw new Error(
|
|
2350
2486
|
'Contract declarations are out of sync. Run ' +
|
|
@@ -350,9 +350,16 @@ value to GitHub secrets, workflow input, argv, or agent-visible material.
|
|
|
350
350
|
|
|
351
351
|
The protected deploy role needs metadata authority for the Slice prefix:
|
|
352
352
|
\`ListSecrets\`, \`DescribeSecret\`, \`CreateSecret\`, and \`TagResource\`.
|
|
353
|
-
It
|
|
354
|
-
|
|
355
|
-
|
|
353
|
+
It is denied \`BatchGetSecretValue\`, \`UpdateSecretVersionStage\`, and
|
|
354
|
+
application deployment outside its separately reviewed deploy policy, and it is
|
|
355
|
+
denied \`GetSecretValue\` and \`PutSecretValue\` for every operator and
|
|
356
|
+
generated secret; the exact tagged Foundation database credential is the only
|
|
357
|
+
value-action exception. \`UpdateSecret\` is permitted only inside the stage's
|
|
358
|
+
\`ebk/<stage>/*\` namespace and the Foundation provider secret, so native
|
|
359
|
+
recovery can reconcile EBK-owned containers. \`CreateSecret\` and
|
|
360
|
+
\`UpdateSecret\` can both carry a value and IAM cannot prove a call is
|
|
361
|
+
metadata-only, so reviewed generated source, temporary deployment credentials,
|
|
362
|
+
and CloudTrail are the controls.
|
|
356
363
|
\`DeleteSecret\` is used only by the governed generated-secret retirement path
|
|
357
364
|
after native Slice removal succeeds; the executor binds that call to the exact
|
|
358
365
|
generated HMAC name and ownership tags described above.
|
|
@@ -300,7 +300,7 @@ RUN --network=none pnpm --config.inject-workspace-packages=true --config.store-d
|
|
|
300
300
|
|
|
301
301
|
FROM ${frozen_container_js_1.WORKLOAD_CONTAINER_NODE_IMAGE}
|
|
302
302
|
WORKDIR /app
|
|
303
|
-
ADD --chown=node:node --chmod=0444 --checksum=sha256:
|
|
303
|
+
ADD --chown=node:node --chmod=0444 --checksum=sha256:90a6f1a7978a1b1b096dca6c57511ca48d3b8cf052f8e704baf358f34ea2f21b https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem ./rds-global-bundle.pem
|
|
304
304
|
COPY --from=build --chown=node:node /opt/migration ./
|
|
305
305
|
COPY --chown=node:node packages/${name}/jobs/database-lifecycle-logging.mjs ./jobs/database-lifecycle-logging.mjs
|
|
306
306
|
COPY --chown=node:node packages/${name}/jobs/bootstrap-database.mjs ./jobs/bootstrap-database.mjs
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal-only evaluator for a Slice's canonical EventContractSchema export.
|
|
3
|
+
* Release compatibility and the cross-Slice subscription check render the
|
|
4
|
+
* same source so both read a producer contract identically.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EVENT_CONTRACT_LITERAL_SOURCE = "const eventLiteral = (node) => {\n while (\n ts.isAsExpression(node) ||\n ts.isSatisfiesExpression(node) ||\n ts.isParenthesizedExpression(node)\n ) {\n node = node.expression;\n }\n if (ts.isObjectLiteralExpression(node)) {\n return Object.fromEntries(\n node.properties.map((property) => {\n if (\n !ts.isPropertyAssignment(property) ||\n !(\n ts.isIdentifier(property.name) ||\n ts.isStringLiteral(property.name)\n )\n ) {\n throw new Error(\n 'EventContractSchema accepts only explicit literal properties.',\n );\n }\n return [property.name.text, eventLiteral(property.initializer)];\n }),\n );\n }\n if (ts.isArrayLiteralExpression(node)) {\n return node.elements.map(eventLiteral);\n }\n if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {\n return ts.isNumericLiteral(node) ? Number(node.text) : node.text;\n }\n if (\n node.kind === ts.SyntaxKind.TrueKeyword ||\n node.kind === ts.SyntaxKind.FalseKeyword\n ) {\n return node.kind === ts.SyntaxKind.TrueKeyword;\n }\n throw new Error(\n 'EventContractSchema must remain a canonical literal-only export.',\n );\n};\n";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVENT_CONTRACT_LITERAL_SOURCE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Literal-only evaluator for a Slice's canonical EventContractSchema export.
|
|
6
|
+
* Release compatibility and the cross-Slice subscription check render the
|
|
7
|
+
* same source so both read a producer contract identically.
|
|
8
|
+
*/
|
|
9
|
+
exports.EVENT_CONTRACT_LITERAL_SOURCE = `const eventLiteral = (node) => {
|
|
10
|
+
while (
|
|
11
|
+
ts.isAsExpression(node) ||
|
|
12
|
+
ts.isSatisfiesExpression(node) ||
|
|
13
|
+
ts.isParenthesizedExpression(node)
|
|
14
|
+
) {
|
|
15
|
+
node = node.expression;
|
|
16
|
+
}
|
|
17
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
18
|
+
return Object.fromEntries(
|
|
19
|
+
node.properties.map((property) => {
|
|
20
|
+
if (
|
|
21
|
+
!ts.isPropertyAssignment(property) ||
|
|
22
|
+
!(
|
|
23
|
+
ts.isIdentifier(property.name) ||
|
|
24
|
+
ts.isStringLiteral(property.name)
|
|
25
|
+
)
|
|
26
|
+
) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'EventContractSchema accepts only explicit literal properties.',
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return [property.name.text, eventLiteral(property.initializer)];
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
36
|
+
return node.elements.map(eventLiteral);
|
|
37
|
+
}
|
|
38
|
+
if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
|
|
39
|
+
return ts.isNumericLiteral(node) ? Number(node.text) : node.text;
|
|
40
|
+
}
|
|
41
|
+
if (
|
|
42
|
+
node.kind === ts.SyntaxKind.TrueKeyword ||
|
|
43
|
+
node.kind === ts.SyntaxKind.FalseKeyword
|
|
44
|
+
) {
|
|
45
|
+
return node.kind === ts.SyntaxKind.TrueKeyword;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(
|
|
48
|
+
'EventContractSchema must remain a canonical literal-only export.',
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
`;
|
|
52
|
+
//# sourceMappingURL=built-in-plan-event-contract-literal.js.map
|