@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.
Files changed (100) hide show
  1. package/package.json +3 -3
  2. package/src/blueprints/external-sandbox.d.ts +1 -1
  3. package/src/cli/main.d.ts +11 -5
  4. package/src/cli/main.js +88 -29
  5. package/src/container-images.d.ts +1 -1
  6. package/src/container-images.js +1 -1
  7. package/src/developer-agent/generator-planning.js +10 -3
  8. package/src/developer-agent/operation-registry.js +1 -1
  9. package/src/executors/deployment-plan/deployment-plan.js +5 -1
  10. package/src/executors/deployment-plan/schema.json +1 -1
  11. package/src/executors/fleet/fleet.js +4 -0
  12. package/src/executors/fleet/schema.json +1 -1
  13. package/src/executors/github-bootstrap/github-bootstrap.d.ts +3 -0
  14. package/src/executors/github-bootstrap/github-bootstrap.js +28 -0
  15. package/src/executors/github-bootstrap/schema.json +14 -0
  16. package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
  17. package/src/executors/local-deploy/federated-credential-transport.js +15 -5
  18. package/src/foundation/cost-estimate.d.ts +8 -3
  19. package/src/foundation/cost-estimate.js +42 -19
  20. package/src/foundation/index.d.ts +1 -0
  21. package/src/foundation/index.js +1 -0
  22. package/src/foundation/local-private-smoke-access.js +6 -0
  23. package/src/foundation/logging.d.ts +3 -1
  24. package/src/foundation/logging.js +8 -1
  25. package/src/foundation/observability.d.ts +42 -0
  26. package/src/foundation/observability.js +181 -0
  27. package/src/foundation/preflight.js +1 -1
  28. package/src/generators/function/function.js +5 -2
  29. package/src/generators/observability-infrastructure.d.ts +2 -0
  30. package/src/generators/observability-infrastructure.js +31 -0
  31. package/src/generators/observability-policy.d.ts +4 -0
  32. package/src/generators/observability-policy.js +23 -0
  33. package/src/generators/preset/dependency-overrides.d.ts +17 -6
  34. package/src/generators/preset/dependency-overrides.js +17 -6
  35. package/src/generators/preset/tool-versions.d.ts +2 -2
  36. package/src/generators/preset/tool-versions.js +2 -2
  37. package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
  38. package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
  39. package/src/generators/preset/workspace-files-agent-development.js +42 -0
  40. package/src/generators/preset/workspace-files-thin-deploy.js +17 -4
  41. package/src/generators/preset/workspace-files-thin-docs.js +10 -22
  42. package/src/generators/preset/workspace-files.d.ts +1 -0
  43. package/src/generators/preset/workspace-files.js +120 -35
  44. package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
  45. package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
  46. package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
  47. package/src/generators/slice/built-in-plan-data-files.js +10 -3
  48. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
  49. package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
  50. package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
  51. package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
  52. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
  53. package/src/generators/slice/built-in-plan-metadata-files.js +37 -2
  54. package/src/generators/slice/built-in-plan-operations-files.js +106 -6
  55. package/src/generators/slice/built-in-plan-project-files.js +1 -0
  56. package/src/generators/slice/built-in-plan-release-files.js +2 -44
  57. package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
  58. package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
  59. package/src/generators/slice/built-in-plan-shared.js +29 -2
  60. package/src/generators/slice/built-in-plan-verification-files.js +0 -1
  61. package/src/generators/slice/built-in-plan-web-files.js +22 -6
  62. package/src/generators/slice/built-in-plan.js +2 -2
  63. package/src/generators/slice/slice.js +2 -0
  64. package/src/generators/upgrade/upgrade.d.ts +4 -3
  65. package/src/generators/upgrade/upgrade.js +139 -29
  66. package/src/generators/workload/frozen-container.d.ts +1 -1
  67. package/src/generators/workload/reliability-registration.d.ts +1 -1
  68. package/src/generators/workload/reliability-registration.js +9 -9
  69. package/src/generators/workload/workload.d.ts +1 -1
  70. package/src/generators/workload/workload.js +32 -10
  71. package/src/git-policy/github-bootstrap.d.ts +18 -0
  72. package/src/git-policy/github-bootstrap.js +99 -6
  73. package/src/local-dev/dev-command-core.d.ts +24 -3
  74. package/src/local-dev/dev-command-core.js +124 -12
  75. package/src/local-dev/errors.d.ts +19 -1
  76. package/src/local-dev/errors.js +18 -1
  77. package/src/local-dev/fleet-client.d.ts +64 -6
  78. package/src/local-dev/fleet-client.js +189 -15
  79. package/src/local-dev/fleet-process.d.ts +47 -2
  80. package/src/local-dev/fleet-process.js +248 -31
  81. package/src/local-dev/fleet-runtime.d.ts +37 -0
  82. package/src/local-dev/fleet-runtime.js +127 -69
  83. package/src/local-dev/fleet-session.d.ts +1 -0
  84. package/src/local-dev/fleet-session.js +83 -7
  85. package/src/local-dev/fleet-state.d.ts +5 -0
  86. package/src/local-dev/fleet-state.js +11 -2
  87. package/src/local-dev/fleet-supervisor-daemon.js +1 -5
  88. package/src/local-dev/fleet-supervisor.d.ts +7 -0
  89. package/src/local-dev/fleet-supervisor.js +83 -12
  90. package/src/local-dev/slice-runner.d.ts +19 -1
  91. package/src/local-dev/slice-runner.js +290 -91
  92. package/src/repository-policy/deployment-plan.d.ts +1 -1
  93. package/src/repository-policy/deployment-plan.js +16 -1
  94. package/src/secrets/generated-aws-control-plane.d.ts +11 -1
  95. package/src/secrets/generated-aws-control-plane.js +92 -29
  96. package/src/secrets/operator.d.ts +3 -2
  97. package/src/secrets/operator.js +27 -5
  98. package/src/supply-chain/blueprint-verification-authority.js +24 -3
  99. package/src/supply-chain/generated-files.js +9 -0
  100. package/src/supply-chain/sbom.js +74 -12
@@ -2,11 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FOUNDATION_COST_ESTIMATE_END_MARKER = exports.FOUNDATION_COST_ESTIMATE_BEGIN_MARKER = exports.STANDARD_SLICE_COST_INVENTORY = exports.FOUNDATION_COST_INVENTORY = exports.FOUNDATION_LIST_PRICE_SNAPSHOT = void 0;
4
4
  exports.foundationCostEstimateLabel = foundationCostEstimateLabel;
5
+ exports.observabilityDashboardInventory = observabilityDashboardInventory;
5
6
  exports.nonproductionAuroraScalingLabel = nonproductionAuroraScalingLabel;
6
7
  exports.renderFoundationCostEstimate = renderFoundationCostEstimate;
7
8
  const index_js_1 = require("../repository-policy/index.js");
8
9
  const capability_plan_js_1 = require("./capability-plan.js");
9
10
  const router_waf_js_1 = require("./router-waf.js");
11
+ const observability_js_1 = require("./observability.js");
10
12
  function price(usd, unit, offer, version, sku, rate, usageType, options = {}) {
11
13
  return {
12
14
  per: options.per ?? 1,
@@ -91,8 +93,8 @@ exports.FOUNDATION_LIST_PRICE_SNAPSHOT = {
91
93
  exports.FOUNDATION_COST_INVENTORY = {
92
94
  /** One CloudFront Router Web ACL per stage. */
93
95
  wafWebAcls: 1,
94
- /** One Foundation CloudWatch dashboard per stage. */
95
- dashboards: 1,
96
+ /** Cost-optimized has no paid dashboard; richer profiles have one stage dashboard. */
97
+ dashboards: 0,
96
98
  /** Declared network: `az: 2`, one Elastic IP and one NAT unit per public subnet. */
97
99
  availabilityZones: 2,
98
100
  /** SST Vpc Cloud Map private DNS namespace (a Route 53 private hosted zone). */
@@ -122,28 +124,28 @@ exports.FOUNDATION_COST_INVENTORY = {
122
124
  */
123
125
  exports.STANDARD_SLICE_COST_INVENTORY = {
124
126
  sqlFree: {
125
- alarms: 54,
126
- alarmMetrics: 63,
127
- dashboards: 1,
128
- dataProtectedLogGroups: 28,
127
+ alarms: 52,
128
+ alarmMetrics: 61,
129
+ dashboards: 0,
130
+ dataProtectedLogGroups: 26,
129
131
  kmsKeys: 1,
130
132
  machineClients: 1,
131
133
  secrets: 1,
132
134
  },
133
135
  sqlDeclared: {
134
- alarms: 61,
135
- alarmMetrics: 70,
136
- dashboards: 1,
137
- dataProtectedLogGroups: 32,
136
+ alarms: 59,
137
+ alarmMetrics: 68,
138
+ dashboards: 0,
139
+ dataProtectedLogGroups: 30,
138
140
  kmsKeys: 1,
139
141
  machineClients: 1,
140
142
  secrets: 3,
141
143
  },
142
144
  personalStage: {
143
- alarms: 50,
144
- alarmMetrics: 57,
145
- dashboards: 1,
146
- dataProtectedLogGroups: 25,
145
+ alarms: 48,
146
+ alarmMetrics: 55,
147
+ dashboards: 0,
148
+ dataProtectedLogGroups: 23,
147
149
  kmsKeys: 1,
148
150
  machineClients: 1,
149
151
  secrets: 1,
@@ -263,11 +265,22 @@ function natMode(stage, profile) {
263
265
  ? index_js_1.PRODUCTION_FOUNDATION_PROFILE_CONTRACTS[profile].nat
264
266
  : exports.FOUNDATION_COST_INVENTORY.nonproductionNat;
265
267
  }
268
+ function observabilityDashboardInventory(stage, observabilityProfile = 'cost-optimized') {
269
+ const policy = (0, observability_js_1.resolveObservabilityPolicy)({
270
+ policy: (0, observability_js_1.defaultObservabilityPolicy)(['development', 'staging', 'production'], { productionProfile: observabilityProfile }),
271
+ stage,
272
+ productionProfile: observabilityProfile,
273
+ });
274
+ return {
275
+ foundation: policy.dashboards === 'none' ? 0 : 1,
276
+ slice: policy.dashboards === 'slice' ? 1 : 0,
277
+ };
278
+ }
266
279
  function stageFloors(stage, profile) {
267
280
  const inventory = exports.FOUNDATION_COST_INVENTORY;
268
281
  const sqlFree = monthly('wafWebAcl', inventory.wafWebAcls) +
269
282
  monthly('wafRule', wafRuleCount(stage)) +
270
- monthly('cloudWatchDashboard', inventory.dashboards);
283
+ monthly('cloudWatchDashboard', observabilityDashboardInventory(stage).foundation);
271
284
  const nat = natMode(stage, profile);
272
285
  const network = sqlFree +
273
286
  monthly('route53HostedZone', inventory.privateHostedZones) +
@@ -344,7 +357,7 @@ function renderFoundationCostEstimate(options) {
344
357
  [
345
358
  'CloudWatch dashboard',
346
359
  p('cloudWatchDashboard'),
347
- ...perStage(() => cell(monthly('cloudWatchDashboard', inventory.dashboards), inventory.dashboards)),
360
+ ...perStage((stage) => cell(monthly('cloudWatchDashboard', observabilityDashboardInventory(stage, defaultProfile).foundation), observabilityDashboardInventory(stage, defaultProfile).foundation)),
348
361
  ],
349
362
  [
350
363
  '**SQL-free stage floor**',
@@ -555,14 +568,18 @@ function renderFoundationCostEstimate(options) {
555
568
  [
556
569
  'CloudWatch Logs ingestion',
557
570
  p('cloudWatchLogsIngest'),
558
- 'TRACE application logging on every function',
571
+ 'JSON application logs; only explicitly marked routine success diagnostics are sampled',
559
572
  ],
560
573
  [
561
574
  'CloudWatch Logs data protection',
562
575
  p('cloudWatchLogsDataProtection'),
563
576
  'Every byte written to a data-protected log group',
564
577
  ],
565
- ['CloudWatch Logs storage', p('cloudWatchLogsStorage'), '30-day retention'],
578
+ [
579
+ 'CloudWatch Logs storage',
580
+ p('cloudWatchLogsStorage'),
581
+ 'At least 30 days for mixed security/receipt groups; see ebk.observability.json',
582
+ ],
566
583
  [
567
584
  'CloudWatch custom metrics',
568
585
  p('cloudWatchCustomMetric'),
@@ -636,6 +653,8 @@ function renderFoundationCostEstimate(options) {
636
653
  '',
637
654
  heading(level + 1, 'Production Aurora capacity profiles'),
638
655
  '',
656
+ 'These capacity comparisons keep the independently seeded cost-optimized production observability policy. Selecting more Aurora capacity does not add dashboards or change log retention.',
657
+ '',
639
658
  table([
640
659
  'Capacity profile',
641
660
  'Aurora range',
@@ -647,7 +666,11 @@ function renderFoundationCostEstimate(options) {
647
666
  '',
648
667
  heading(level + 1, 'Standard Slice fixed floor per stage'),
649
668
  '',
650
- 'Every deployed stage of every default Standard Slice adds this floor (no async adapters, performance load off).',
669
+ 'Every deployed stage of every default Standard Slice adds this floor (no async adapters, performance load off). This table uses cost-optimized/balanced Slice defaults: no Slice dashboard. Standard adds one per Slice, priced below.',
670
+ '',
671
+ `Observability: cost-optimized adds no dashboard; balanced adds one Foundation dashboard; standard adds one Foundation dashboard and one per Slice (${money(monthly('cloudWatchDashboard', 1))} each per month). See ebk.observability.json; upgraded explicit legacy dashboard choices have the standard dashboard count. Core failure/security alarms remain in every profile.`,
672
+ '',
673
+ `Fresh defaults omit the two never-created async provider/webhook component log groups while adapters are disabled. Enabling adapters or retaining dormant component logs adds 2 data-protected groups and 2 findings alarms (2 alarm metrics, ${money(monthly('cloudWatchAlarmMetric', 2))} per month). Legacy stage policies retain these groups to preserve possible log history, including personal stages; fresh personal stages omit them unless preservation is configured. Provider recovery queues and their failure alarms remain in every case.`,
651
674
  '',
652
675
  table(['Item', 'Unit list price', 'SQL-free Slice', 'SQL-declared Slice'], sliceRows, ['left', 'left', 'right', 'right']),
653
676
  '',
@@ -2,6 +2,7 @@ export * from './discovery.js';
2
2
  export * from './capability-plan.js';
3
3
  export * from './local-database-contract.js';
4
4
  export * from './logging.js';
5
+ export * from './observability.js';
5
6
  export * from './logging-preflight.js';
6
7
  export * from './partial-state.js';
7
8
  export * from './provision-database-bindings.js';
@@ -6,6 +6,7 @@ tslib_1.__exportStar(require("./discovery.js"), exports);
6
6
  tslib_1.__exportStar(require("./capability-plan.js"), exports);
7
7
  tslib_1.__exportStar(require("./local-database-contract.js"), exports);
8
8
  tslib_1.__exportStar(require("./logging.js"), exports);
9
+ tslib_1.__exportStar(require("./observability.js"), exports);
9
10
  tslib_1.__exportStar(require("./logging-preflight.js"), exports);
10
11
  tslib_1.__exportStar(require("./partial-state.js"), exports);
11
12
  tslib_1.__exportStar(require("./provision-database-bindings.js"), exports);
@@ -169,6 +169,12 @@ async function runLocalPrivateSmoke(input, dependencies = {}) {
169
169
  }
170
170
  throw new Error(`Local private smoke: ${allowed ? 'admission' : 'revocation'} not observed; deployment acceptance prohibited`);
171
171
  }
172
+ // On cancellation this runs after the priority-200 transport cleanup has
173
+ // deleted the credential files. It relies on the WAF client opened above:
174
+ // the SDK memoizes resolved credentials per client, and credentials read
175
+ // from an ini file carry no expiry, so they are never re-read. Do not
176
+ // construct a new client or credential provider in this cleanup path; an
177
+ // unrevoked admission otherwise waits for next-run receipt recovery.
172
178
  const unregister = (0, federated_credential_transport_js_1.registerGovernedSignalCleanup)(async () => {
173
179
  await (0, private_smoke_admission_js_1.cleanupSmokeAdmission)(store, context);
174
180
  }, 90);
@@ -12,6 +12,8 @@ export declare const FORBIDDEN_DEPLOYMENT_LOG_POLICY_ACTIONS: readonly ["logs:De
12
12
  export interface ManagedLogGroupInput {
13
13
  component: string;
14
14
  resource?: string;
15
+ /** These groups mix diagnostics with security and delivery receipts. */
16
+ retentionInDays?: number;
15
17
  slice: string;
16
18
  stage: string;
17
19
  workspace: string;
@@ -44,7 +46,7 @@ export interface ManagedLogGroupContract {
44
46
  };
45
47
  logGroupClass: typeof MANAGED_LOG_GROUP_CLASS;
46
48
  name: string;
47
- retentionInDays: typeof MANAGED_LOG_GROUP_RETENTION_DAYS;
49
+ retentionInDays: number;
48
50
  tags: Record<string, string>;
49
51
  }
50
52
  export interface ObservedManagedLogGroup {
@@ -43,6 +43,13 @@ function requireWorkspace(value) {
43
43
  }
44
44
  function createManagedLogGroupContract(input) {
45
45
  const component = requireSegment(input.component, 'component');
46
+ const retentionInDays = input.retentionInDays ?? exports.MANAGED_LOG_GROUP_RETENTION_DAYS;
47
+ if (![
48
+ 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557,
49
+ 2922, 3288, 3653,
50
+ ].includes(retentionInDays)) {
51
+ throw new Error('[foundation/logging] Mixed security/receipt log groups require an AWS-supported retention of at least 30 days.');
52
+ }
46
53
  const slice = requireSegment(input.slice, 'slice');
47
54
  const stage = requireSegment(input.stage, 'stage');
48
55
  const workspace = requireWorkspace(input.workspace);
@@ -79,7 +86,7 @@ function createManagedLogGroupContract(input) {
79
86
  },
80
87
  logGroupClass: exports.MANAGED_LOG_GROUP_CLASS,
81
88
  name,
82
- retentionInDays: exports.MANAGED_LOG_GROUP_RETENTION_DAYS,
89
+ retentionInDays,
83
90
  tags: {
84
91
  [exports.MANAGED_LOG_GROUP_TAG_KEYS.component]: component,
85
92
  [exports.MANAGED_LOG_GROUP_TAG_KEYS.managed]: 'true',
@@ -0,0 +1,42 @@
1
+ import { type FoundationStage } from './stages.js';
2
+ export declare const OBSERVABILITY_POLICY_PATH = "ebk.observability.json";
3
+ export declare const OBSERVABILITY_PROFILES: readonly ["cost-optimized", "balanced", "standard"];
4
+ export type ObservabilityProfile = (typeof OBSERVABILITY_PROFILES)[number];
5
+ export type DashboardPolicy = 'none' | 'stage' | 'slice';
6
+ export interface ObservabilityStagePolicy {
7
+ /** Explicitly seeded; an omitted production value requires a supplied capacity profile. */
8
+ profile?: ObservabilityProfile;
9
+ routineSuccessSampleRate?: number;
10
+ diagnosticRetentionDays?: number;
11
+ protectedRetentionDays?: number;
12
+ dashboards?: DashboardPolicy;
13
+ systemLogLevel?: 'WARN' | 'INFO';
14
+ /** Preserve groups with possible historical logs when adapters are disabled. */
15
+ retainDormantComponentLogs?: boolean;
16
+ }
17
+ export interface ObservabilityPolicy {
18
+ schemaVersion: 1;
19
+ stages: Partial<Record<FoundationStage, ObservabilityStagePolicy>>;
20
+ }
21
+ export interface ResolvedObservabilityPolicy {
22
+ profile: ObservabilityProfile;
23
+ routineSuccessSampleRate: number;
24
+ diagnosticRetentionDays: number;
25
+ /** Shared application/security/receipt groups use this floor, not diagnostic retention. */
26
+ protectedRetentionDays: number;
27
+ dashboards: DashboardPolicy;
28
+ systemLogLevel: 'WARN' | 'INFO';
29
+ retainDormantComponentLogs: boolean;
30
+ }
31
+ export declare function parseObservabilityPolicy(value: unknown, activeStages?: readonly FoundationStage[]): ObservabilityPolicy;
32
+ /** New workspaces opt in; a managed upgrade of an older workspace preserves its choices. */
33
+ export declare function defaultObservabilityPolicy(stages: readonly FoundationStage[], options?: {
34
+ preserveLegacy?: boolean;
35
+ productionProfile?: ObservabilityProfile;
36
+ }): ObservabilityPolicy;
37
+ export declare function resolveObservabilityPolicy(input: {
38
+ policy: unknown;
39
+ stage: string;
40
+ productionProfile?: string;
41
+ local?: boolean;
42
+ }): ResolvedObservabilityPolicy;
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OBSERVABILITY_PROFILES = exports.OBSERVABILITY_POLICY_PATH = void 0;
4
+ exports.parseObservabilityPolicy = parseObservabilityPolicy;
5
+ exports.defaultObservabilityPolicy = defaultObservabilityPolicy;
6
+ exports.resolveObservabilityPolicy = resolveObservabilityPolicy;
7
+ const stages_js_1 = require("./stages.js");
8
+ exports.OBSERVABILITY_POLICY_PATH = 'ebk.observability.json';
9
+ exports.OBSERVABILITY_PROFILES = [
10
+ 'cost-optimized',
11
+ 'balanced',
12
+ 'standard',
13
+ ];
14
+ const RETENTION_DAYS = new Set([
15
+ 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557,
16
+ 2922, 3288, 3653,
17
+ ]);
18
+ const STAGE_KEYS = new Set(['development', 'staging', 'production']);
19
+ const FIELD_KEYS = new Set([
20
+ 'profile',
21
+ 'routineSuccessSampleRate',
22
+ 'diagnosticRetentionDays',
23
+ 'protectedRetentionDays',
24
+ 'dashboards',
25
+ 'systemLogLevel',
26
+ 'retainDormantComponentLogs',
27
+ ]);
28
+ const PROFILE_DEFAULTS = {
29
+ 'cost-optimized': {
30
+ sampleRate: 0.05,
31
+ diagnosticDays: 7,
32
+ productionDiagnosticDays: 30,
33
+ dashboards: 'none',
34
+ systemLogLevel: 'WARN',
35
+ },
36
+ balanced: {
37
+ sampleRate: 0.25,
38
+ diagnosticDays: 14,
39
+ productionDiagnosticDays: 30,
40
+ dashboards: 'stage',
41
+ systemLogLevel: 'INFO',
42
+ },
43
+ standard: {
44
+ sampleRate: 1,
45
+ diagnosticDays: 30,
46
+ productionDiagnosticDays: 90,
47
+ dashboards: 'slice',
48
+ systemLogLevel: 'INFO',
49
+ },
50
+ };
51
+ function record(value, label) {
52
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
53
+ throw new Error(`[observability] ${label} must be an object.`);
54
+ }
55
+ return value;
56
+ }
57
+ function parseObservabilityPolicy(value, activeStages) {
58
+ const root = record(value, 'Policy');
59
+ if (root.schemaVersion !== 1 ||
60
+ Object.keys(root).some((key) => !['schemaVersion', 'stages'].includes(key))) {
61
+ throw new Error('[observability] Expected schemaVersion 1 with stages only.');
62
+ }
63
+ const stages = record(root.stages, 'stages');
64
+ if (Object.keys(stages).length === 0)
65
+ throw new Error('[observability] Declare active stages.');
66
+ if (activeStages &&
67
+ (Object.keys(stages).length !== activeStages.length ||
68
+ activeStages.some((stage) => !Object.prototype.hasOwnProperty.call(stages, stage))))
69
+ throw new Error('[observability] Declare exactly the active repository stages: ' +
70
+ activeStages.join(', ') +
71
+ '. Update seeded ebk.observability.json before generation or upgrade.');
72
+ for (const [stage, raw] of Object.entries(stages)) {
73
+ if (!STAGE_KEYS.has(stage))
74
+ throw new Error(`[observability] Unsupported stage ${stage}.`);
75
+ const entry = record(raw, stage);
76
+ if (Object.keys(entry).some((key) => !FIELD_KEYS.has(key)))
77
+ throw new Error(`[observability] Unknown field in ${stage}.`);
78
+ if (entry.profile !== undefined &&
79
+ !exports.OBSERVABILITY_PROFILES.includes(entry.profile))
80
+ throw new Error(`[observability] Invalid profile for ${stage}.`);
81
+ const sample = entry.routineSuccessSampleRate;
82
+ if (sample !== undefined &&
83
+ (typeof sample !== 'number' ||
84
+ !Number.isFinite(sample) ||
85
+ sample < 0 ||
86
+ sample > 1))
87
+ throw new Error(`[observability] ${stage} routineSuccessSampleRate must be between 0 and 1.`);
88
+ for (const key of [
89
+ 'diagnosticRetentionDays',
90
+ 'protectedRetentionDays',
91
+ ]) {
92
+ const days = entry[key];
93
+ if (days !== undefined &&
94
+ (typeof days !== 'number' ||
95
+ !RETENTION_DAYS.has(days) ||
96
+ (key === 'protectedRetentionDays' && days < 30)))
97
+ throw new Error(`[observability] Invalid ${stage} ${key}; protected logs require at least 30 days.`);
98
+ }
99
+ if (entry.dashboards !== undefined &&
100
+ (typeof entry.dashboards !== 'string' ||
101
+ !['none', 'stage', 'slice'].includes(entry.dashboards)))
102
+ throw new Error(`[observability] Invalid dashboard policy for ${stage}.`);
103
+ if (entry.systemLogLevel !== undefined &&
104
+ (typeof entry.systemLogLevel !== 'string' ||
105
+ !['WARN', 'INFO'].includes(entry.systemLogLevel)))
106
+ throw new Error(`[observability] Invalid systemLogLevel for ${stage}.`);
107
+ if (entry.retainDormantComponentLogs !== undefined &&
108
+ typeof entry.retainDormantComponentLogs !== 'boolean')
109
+ throw new Error(`[observability] Invalid retainDormantComponentLogs for ${stage}.`);
110
+ }
111
+ return JSON.parse(JSON.stringify(root));
112
+ }
113
+ /** New workspaces opt in; a managed upgrade of an older workspace preserves its choices. */
114
+ function defaultObservabilityPolicy(stages, options = {}) {
115
+ const value = { schemaVersion: 1, stages: {} };
116
+ for (const stage of stages) {
117
+ value.stages[stage] = options.preserveLegacy
118
+ ? {
119
+ profile: 'standard',
120
+ routineSuccessSampleRate: 1,
121
+ diagnosticRetentionDays: 30,
122
+ protectedRetentionDays: 30,
123
+ dashboards: 'slice',
124
+ systemLogLevel: 'INFO',
125
+ retainDormantComponentLogs: true,
126
+ }
127
+ : stage === 'production'
128
+ ? { profile: options.productionProfile ?? 'cost-optimized' }
129
+ : { profile: stage === 'development' ? 'cost-optimized' : 'balanced' };
130
+ }
131
+ return parseObservabilityPolicy(value);
132
+ }
133
+ function resolveObservabilityPolicy(input) {
134
+ const policy = parseObservabilityPolicy(input.policy);
135
+ const resolved = (0, stages_js_1.resolveFoundationStages)({
136
+ consumerStage: input.stage,
137
+ activeFoundationStages: Object.keys(policy.stages),
138
+ });
139
+ const entry = policy.stages[resolved.foundationStage];
140
+ if (!entry)
141
+ throw new Error('[observability] Resolved Foundation stage has no policy.');
142
+ const inherited = resolved.foundationStage === 'production'
143
+ ? input.productionProfile
144
+ : resolved.foundationStage === 'staging'
145
+ ? 'balanced'
146
+ : 'cost-optimized';
147
+ const profile = resolved.mapped
148
+ ? 'cost-optimized'
149
+ : (entry.profile ?? inherited);
150
+ if (!exports.OBSERVABILITY_PROFILES.includes(profile))
151
+ throw new Error('[observability] Production observability requires an explicit profile or a valid committed production capacity profile.');
152
+ const defaults = PROFILE_DEFAULTS[profile];
153
+ const production = resolved.foundationStage === 'production';
154
+ // Task stages never acquire paid dashboards or verbose cloud system logs just
155
+ // because the shared development stage uses a richer profile.
156
+ // Lean task diagnostics do not authorize shortening explicit retention or
157
+ // removing groups with historical logs. Those preservation choices survive.
158
+ const overrides = resolved.mapped
159
+ ? {
160
+ diagnosticRetentionDays: entry.diagnosticRetentionDays,
161
+ protectedRetentionDays: entry.protectedRetentionDays,
162
+ retainDormantComponentLogs: entry.retainDormantComponentLogs,
163
+ }
164
+ : entry;
165
+ return {
166
+ profile: profile,
167
+ routineSuccessSampleRate: input.local
168
+ ? 1
169
+ : (overrides.routineSuccessSampleRate ?? defaults.sampleRate),
170
+ diagnosticRetentionDays: overrides.diagnosticRetentionDays ??
171
+ (production
172
+ ? defaults.productionDiagnosticDays
173
+ : defaults.diagnosticDays),
174
+ protectedRetentionDays: overrides.protectedRetentionDays ??
175
+ (production && profile === 'standard' ? 90 : 30),
176
+ dashboards: overrides.dashboards ?? defaults.dashboards,
177
+ systemLogLevel: overrides.systemLogLevel ?? defaults.systemLogLevel,
178
+ retainDormantComponentLogs: overrides.retainDormantComponentLogs ?? false,
179
+ };
180
+ }
181
+ //# sourceMappingURL=observability.js.map
@@ -181,7 +181,7 @@ function formatFoundationPreflightReport(report) {
181
181
  report.issues.forEach((issue) => lines.push(` - ${issue}`));
182
182
  if (!report.ok) {
183
183
  lines.push(report.foundationStage === 'development'
184
- ? ' Establish Foundation through ebk deploy development --profile=<approved-profile> --expectedRoleArn=<approved-development-role>; the governed stage lifecycle deploys Foundation before eligible Slices.'
184
+ ? ' Establish Foundation through ebk deploy development --profile=<development-profile> --expectedRoleArn=<role-arn>; the governed stage lifecycle deploys Foundation before eligible Slices.'
185
185
  : ` Establish Foundation through the governed ${report.foundationStage} stage lifecycle under the repository deployment profile; follow its protected deployment runbook.`);
186
186
  }
187
187
  return lines.join('\n');
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createFunctionFilePlan = createFunctionFilePlan;
4
4
  exports.functionGenerator = functionGenerator;
5
+ const observability_infrastructure_js_1 = require("../observability-infrastructure.js");
6
+ const observability_policy_js_1 = require("../observability-policy.js");
5
7
  const node_path_1 = require("node:path");
6
8
  const index_js_1 = require("../../ownership/index.js");
7
9
  const feature_registry_js_1 = require("../feature-registry.js");
@@ -50,9 +52,9 @@ function createFunctionFilePlan(workspace, slice, name, feature = name) {
50
52
  !infrastructureSource.includes(' ...(role ?? {}),')) {
51
53
  throw new Error('Built-in Function infrastructure is missing its native permission integration point.');
52
54
  }
53
- files[infrastructurePath] = infrastructureSource
55
+ files[infrastructurePath] = (0, observability_infrastructure_js_1.renderExtensionObservability)(infrastructureSource
54
56
  .replace(' role?: { role: string },', ' permissions: any,')
55
- .replace(' ...(role ?? {}),', ' permissions,');
57
+ .replace(' ...(role ?? {}),', ' permissions,'));
56
58
  return {
57
59
  files,
58
60
  paths: [
@@ -78,6 +80,7 @@ async function functionGenerator(tree, options) {
78
80
  const workspace = requireName(typeof packageName === 'string' ? packageName : '', 'Workspace name');
79
81
  const slice = requireName(options.slice, 'Product Slice name');
80
82
  const name = requireName(options.name, 'Function name');
83
+ (0, observability_policy_js_1.requireObservabilityPolicyFromTree)(tree);
81
84
  (0, post_generation_install_js_1.assertPostGenerationInstallWorkspaceAvailable)(tree, `generator:function:${slice}:${name}`);
82
85
  const sliceRoot = node_path_1.posix.join('packages', slice);
83
86
  const handlerPath = node_path_1.posix.join(sliceRoot, 'api', `${name}.ts`);
@@ -0,0 +1,2 @@
1
+ /** Apply the same policy to separately generated Function/Service/Job compositions. */
2
+ export declare function renderExtensionObservability(source: string): string;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderExtensionObservability = renderExtensionObservability;
4
+ /** Apply the same policy to separately generated Function/Service/Job compositions. */
5
+ function renderExtensionObservability(source) {
6
+ const replace = (from, to) => {
7
+ if (!source.includes(from))
8
+ throw new Error('Generated extension is missing its observability integration point.');
9
+ source = source.split(from).join(to);
10
+ };
11
+ replace("import { createManagedLogGroupContract } from '@empire-builder-kit/nx/foundation';", "import { createManagedLogGroupContract, resolveObservabilityPolicy } from '@empire-builder-kit/nx/foundation';\nimport observabilityPolicy from '../../../../ebk.observability.json';");
12
+ replace(' const contract = createManagedLogGroupContract({', ` const observability = resolveObservabilityPolicy({
13
+ policy: observabilityPolicy,
14
+ stage: $app.stage,
15
+ productionProfile: process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
16
+ local: $dev,
17
+ });
18
+ const contract = createManagedLogGroupContract({
19
+ retentionInDays: observability.protectedRetentionDays,`);
20
+ // These compositions already own their literal environment maps; preserve
21
+ // all application, container-image and configuration bindings.
22
+ replace('EBK_STAGE: $app.stage', 'EBK_OBSERVABILITY_SUCCESS_SAMPLE_RATE: String(observability.routineSuccessSampleRate), EBK_STAGE: $app.stage');
23
+ source = source
24
+ .split("systemLogLevel: 'INFO'")
25
+ .join('systemLogLevel: observability.systemLogLevel');
26
+ source = source
27
+ .split(' logGroup: {')
28
+ .join(' logGroup: {\n retentionInDays: contract.retentionInDays,');
29
+ return source;
30
+ }
31
+ //# sourceMappingURL=observability-infrastructure.js.map
@@ -0,0 +1,4 @@
1
+ import type { Tree } from '@nx/devkit';
2
+ import { type ObservabilityPolicy } from '../foundation/observability.js';
3
+ /** Leaf generators must not invent or repair the adopter's seeded policy. */
4
+ export declare function requireObservabilityPolicyFromTree(tree: Tree): ObservabilityPolicy;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireObservabilityPolicyFromTree = requireObservabilityPolicyFromTree;
4
+ const observability_js_1 = require("../foundation/observability.js");
5
+ const index_js_1 = require("../repository-policy/index.js");
6
+ const tree_policy_js_1 = require("./repository-policy/tree-policy.js");
7
+ /** Leaf generators must not invent or repair the adopter's seeded policy. */
8
+ function requireObservabilityPolicyFromTree(tree) {
9
+ const source = tree.read(observability_js_1.OBSERVABILITY_POLICY_PATH, 'utf8');
10
+ if (source === null) {
11
+ throw new Error(`[observability] ${observability_js_1.OBSERVABILITY_POLICY_PATH} is required before generation or upgrade. Report and apply the workspace upgrade before continuing; leaf generators do not seed this policy.`);
12
+ }
13
+ let value;
14
+ try {
15
+ value = JSON.parse(source);
16
+ }
17
+ catch {
18
+ throw new Error(`[observability] ${observability_js_1.OBSERVABILITY_POLICY_PATH} must be valid JSON. Reconcile the seeded policy before generation or upgrade.`);
19
+ }
20
+ const repositoryPolicy = (0, tree_policy_js_1.readRepositoryOperatingPolicyFromTree)(tree);
21
+ return (0, observability_js_1.parseObservabilityPolicy)(value, (0, index_js_1.activeDeploymentStages)(repositoryPolicy.deploymentProfile));
22
+ }
23
+ //# sourceMappingURL=observability-policy.js.map
@@ -4,25 +4,36 @@ export interface DependencyOverrides {
4
4
  overrides: Record<string, string>;
5
5
  }
6
6
  /**
7
- * Reviewed overrides seeded into a workspace that has no
7
+ * Reviewed Nx and managed OpenNext overrides seeded into a workspace with no
8
8
  * `supply-chain/dependency-overrides.json` yet. Each selector names an exact
9
- * transitive release that the keyed `nx` version pins and that carries a high
10
- * advisory; each target is clean and older than the generated minimum release
11
- * age. The overrides are derived from that one `nx` release, so a spec fails
12
- * when the framework or generated `nx` pin moves and the list must be
13
- * reviewed again. An existing seeded file always wins over these defaults.
9
+ * transitive release with a recorded advisory; each target is clean and older
10
+ * than the generated minimum release age. Each default is keyed to the pin it
11
+ * was reviewed against (`nxVersion`, `openNextVersion`), so a spec fails when
12
+ * that pin moves and the entry must be reviewed again. The OpenNext selector is
13
+ * limited to the minifier parent. An existing seeded file always wins over
14
+ * these defaults.
14
15
  *
15
16
  * A `name@x.y.z` selector is not limited to the `nx` pin. pnpm rewrites every
16
17
  * dependency whose declared range includes x.y.z (in practice a caret or tilde
17
18
  * range on the same major), so minimatch's `brace-expansion` range `^5.0.5`
18
19
  * also resolves 5.0.12. A declared range that excludes x.y.z keeps its own
19
20
  * resolution.
21
+ *
22
+ * A parent selector matches only that exact parent release. `nx` declares its
23
+ * dependencies exactly, but `@opennextjs/aws` declares `@node-minify/core` with
24
+ * a caret, so a later 9.x release that still pins glob 10.3.3 would make the
25
+ * OpenNext entry match nothing without any framework change. The generated
26
+ * workspace's protected scan then denies glob 10.3.3, which is the fail-closed
27
+ * signal to review the entry again.
20
28
  */
21
29
  export declare const DEFAULT_DEPENDENCY_OVERRIDES: {
22
30
  readonly nxVersion: "23.2.1";
31
+ readonly openNextVersion: "4.1.12";
23
32
  readonly overrides: {
33
+ readonly '@node-minify/core@9.0.2>glob@10.3.3': "10.5.0";
24
34
  readonly 'axios@1.18.1': "1.20.0";
25
35
  readonly 'brace-expansion@5.0.9': "5.0.12";
36
+ readonly 'follow-redirects@1.16.0': "1.16.1";
26
37
  readonly 'smol-toml@1.6.1': "1.9.0";
27
38
  };
28
39
  };
@@ -7,25 +7,36 @@ const semver_1 = require("semver");
7
7
  const deterministic_order_js_1 = require("../../deterministic-order.js");
8
8
  exports.DEPENDENCY_OVERRIDES_PATH = 'supply-chain/dependency-overrides.json';
9
9
  /**
10
- * Reviewed overrides seeded into a workspace that has no
10
+ * Reviewed Nx and managed OpenNext overrides seeded into a workspace with no
11
11
  * `supply-chain/dependency-overrides.json` yet. Each selector names an exact
12
- * transitive release that the keyed `nx` version pins and that carries a high
13
- * advisory; each target is clean and older than the generated minimum release
14
- * age. The overrides are derived from that one `nx` release, so a spec fails
15
- * when the framework or generated `nx` pin moves and the list must be
16
- * reviewed again. An existing seeded file always wins over these defaults.
12
+ * transitive release with a recorded advisory; each target is clean and older
13
+ * than the generated minimum release age. Each default is keyed to the pin it
14
+ * was reviewed against (`nxVersion`, `openNextVersion`), so a spec fails when
15
+ * that pin moves and the entry must be reviewed again. The OpenNext selector is
16
+ * limited to the minifier parent. An existing seeded file always wins over
17
+ * these defaults.
17
18
  *
18
19
  * A `name@x.y.z` selector is not limited to the `nx` pin. pnpm rewrites every
19
20
  * dependency whose declared range includes x.y.z (in practice a caret or tilde
20
21
  * range on the same major), so minimatch's `brace-expansion` range `^5.0.5`
21
22
  * also resolves 5.0.12. A declared range that excludes x.y.z keeps its own
22
23
  * resolution.
24
+ *
25
+ * A parent selector matches only that exact parent release. `nx` declares its
26
+ * dependencies exactly, but `@opennextjs/aws` declares `@node-minify/core` with
27
+ * a caret, so a later 9.x release that still pins glob 10.3.3 would make the
28
+ * OpenNext entry match nothing without any framework change. The generated
29
+ * workspace's protected scan then denies glob 10.3.3, which is the fail-closed
30
+ * signal to review the entry again.
23
31
  */
24
32
  exports.DEFAULT_DEPENDENCY_OVERRIDES = {
25
33
  nxVersion: '23.2.1',
34
+ openNextVersion: '4.1.12',
26
35
  overrides: {
36
+ '@node-minify/core@9.0.2>glob@10.3.3': '10.5.0',
27
37
  'axios@1.18.1': '1.20.0',
28
38
  'brace-expansion@5.0.9': '5.0.12',
39
+ 'follow-redirects@1.16.0': '1.16.1',
29
40
  'smol-toml@1.6.1': '1.9.0',
30
41
  },
31
42
  };
@@ -5,9 +5,9 @@
5
5
  * existing workspace explicitly adopts the exact reviewed descriptor.
6
6
  */
7
7
  export declare const WORKSPACE_SST_VERSION: "4.17.1";
8
- export declare const WORKSPACE_NODE_VERSION: "22.23.1";
8
+ export declare const WORKSPACE_NODE_VERSION: "22.23.4";
9
9
  export declare const WORKSPACE_PNPM_VERSION: "11.20.0";
10
- export declare const WORKSPACE_VITEST_VERSION: "4.1.11";
10
+ export declare const WORKSPACE_VITEST_VERSION: "4.1.12";
11
11
  export declare const WORKSPACE_PNPM_SHA512: "9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee";
12
12
  export declare const WORKSPACE_PNPM_DESCRIPTOR: "pnpm@11.20.0+sha512.9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee";
13
13
  /**