@sequenceholdings/pipeline-spec 0.1.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 (126) hide show
  1. package/README.md +114 -0
  2. package/dist/authoring/per-table-ingestion.d.ts +74 -0
  3. package/dist/authoring/per-table-ingestion.d.ts.map +1 -0
  4. package/dist/authoring/per-table-ingestion.js +122 -0
  5. package/dist/authoring/per-table-ingestion.js.map +1 -0
  6. package/dist/authoring/yaml.d.ts +6 -0
  7. package/dist/authoring/yaml.d.ts.map +1 -0
  8. package/dist/authoring/yaml.js +15 -0
  9. package/dist/authoring/yaml.js.map +1 -0
  10. package/dist/binding-gaps.d.ts +21 -0
  11. package/dist/binding-gaps.d.ts.map +1 -0
  12. package/dist/binding-gaps.js +74 -0
  13. package/dist/binding-gaps.js.map +1 -0
  14. package/dist/bodies/ingestion.d.ts +359 -0
  15. package/dist/bodies/ingestion.d.ts.map +1 -0
  16. package/dist/bodies/ingestion.js +446 -0
  17. package/dist/bodies/ingestion.js.map +1 -0
  18. package/dist/bodies/serving.d.ts +316 -0
  19. package/dist/bodies/serving.d.ts.map +1 -0
  20. package/dist/bodies/serving.js +290 -0
  21. package/dist/bodies/serving.js.map +1 -0
  22. package/dist/bodies/transformation.d.ts +397 -0
  23. package/dist/bodies/transformation.d.ts.map +1 -0
  24. package/dist/bodies/transformation.js +278 -0
  25. package/dist/bodies/transformation.js.map +1 -0
  26. package/dist/catalog-binding.d.ts +15 -0
  27. package/dist/catalog-binding.d.ts.map +1 -0
  28. package/dist/catalog-binding.js +15 -0
  29. package/dist/catalog-binding.js.map +1 -0
  30. package/dist/columns.d.ts +177 -0
  31. package/dist/columns.d.ts.map +1 -0
  32. package/dist/columns.js +89 -0
  33. package/dist/columns.js.map +1 -0
  34. package/dist/compatibility.d.ts +25 -0
  35. package/dist/compatibility.d.ts.map +1 -0
  36. package/dist/compatibility.js +108 -0
  37. package/dist/compatibility.js.map +1 -0
  38. package/dist/constants.d.ts +95 -0
  39. package/dist/constants.d.ts.map +1 -0
  40. package/dist/constants.js +105 -0
  41. package/dist/constants.js.map +1 -0
  42. package/dist/diff.d.ts +36 -0
  43. package/dist/diff.d.ts.map +1 -0
  44. package/dist/diff.js +300 -0
  45. package/dist/diff.js.map +1 -0
  46. package/dist/documentation.d.ts +22 -0
  47. package/dist/documentation.d.ts.map +1 -0
  48. package/dist/documentation.js +43 -0
  49. package/dist/documentation.js.map +1 -0
  50. package/dist/envelope.d.ts +1241 -0
  51. package/dist/envelope.d.ts.map +1 -0
  52. package/dist/envelope.js +163 -0
  53. package/dist/envelope.js.map +1 -0
  54. package/dist/errors.d.ts +30 -0
  55. package/dist/errors.d.ts.map +1 -0
  56. package/dist/errors.js +47 -0
  57. package/dist/errors.js.map +1 -0
  58. package/dist/graph-references.d.ts +34 -0
  59. package/dist/graph-references.d.ts.map +1 -0
  60. package/dist/graph-references.js +81 -0
  61. package/dist/graph-references.js.map +1 -0
  62. package/dist/graph.d.ts +112 -0
  63. package/dist/graph.d.ts.map +1 -0
  64. package/dist/graph.js +546 -0
  65. package/dist/graph.js.map +1 -0
  66. package/dist/hash.d.ts +28 -0
  67. package/dist/hash.d.ts.map +1 -0
  68. package/dist/hash.js +91 -0
  69. package/dist/hash.js.map +1 -0
  70. package/dist/index.d.ts +27 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +27 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/issues.d.ts +14 -0
  75. package/dist/issues.d.ts.map +1 -0
  76. package/dist/issues.js +27 -0
  77. package/dist/issues.js.map +1 -0
  78. package/dist/loader.d.ts +50 -0
  79. package/dist/loader.d.ts.map +1 -0
  80. package/dist/loader.js +241 -0
  81. package/dist/loader.js.map +1 -0
  82. package/dist/migrations/contracts.d.ts +38 -0
  83. package/dist/migrations/contracts.d.ts.map +1 -0
  84. package/dist/migrations/contracts.js +175 -0
  85. package/dist/migrations/contracts.js.map +1 -0
  86. package/dist/migrations/manifest.d.ts +137 -0
  87. package/dist/migrations/manifest.d.ts.map +1 -0
  88. package/dist/migrations/manifest.js +137 -0
  89. package/dist/migrations/manifest.js.map +1 -0
  90. package/dist/operations.d.ts +214 -0
  91. package/dist/operations.d.ts.map +1 -0
  92. package/dist/operations.js +275 -0
  93. package/dist/operations.js.map +1 -0
  94. package/dist/orm-satisfies.d.ts +132 -0
  95. package/dist/orm-satisfies.d.ts.map +1 -0
  96. package/dist/orm-satisfies.js +119 -0
  97. package/dist/orm-satisfies.js.map +1 -0
  98. package/dist/primitives.d.ts +11 -0
  99. package/dist/primitives.d.ts.map +1 -0
  100. package/dist/primitives.js +20 -0
  101. package/dist/primitives.js.map +1 -0
  102. package/dist/resolve.d.ts +124 -0
  103. package/dist/resolve.d.ts.map +1 -0
  104. package/dist/resolve.js +194 -0
  105. package/dist/resolve.js.map +1 -0
  106. package/dist/secret-scan.d.ts +34 -0
  107. package/dist/secret-scan.d.ts.map +1 -0
  108. package/dist/secret-scan.js +98 -0
  109. package/dist/secret-scan.js.map +1 -0
  110. package/dist/serving-compatibility.d.ts +14 -0
  111. package/dist/serving-compatibility.d.ts.map +1 -0
  112. package/dist/serving-compatibility.js +19 -0
  113. package/dist/serving-compatibility.js.map +1 -0
  114. package/dist/snapshot-transport.d.ts +83 -0
  115. package/dist/snapshot-transport.d.ts.map +1 -0
  116. package/dist/snapshot-transport.js +42 -0
  117. package/dist/snapshot-transport.js.map +1 -0
  118. package/dist/suggestions.d.ts +3 -0
  119. package/dist/suggestions.d.ts.map +1 -0
  120. package/dist/suggestions.js +26 -0
  121. package/dist/suggestions.js.map +1 -0
  122. package/dist/types.d.ts +72 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +119 -0
  125. package/dist/types.js.map +1 -0
  126. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # @sequenceholdings/pipeline-spec
2
+
3
+ Schema and offline validation support for Sequence Managed Pipeline stage
4
+ specifications.
5
+
6
+ This package is published so `@sequenceholdings/studio-cli` can load its
7
+ pipeline commands in standalone repositories. Its JavaScript module export is
8
+ an internal implementation detail, not a supported public SDK contract. Author
9
+ and validate pipeline repositories through `seq-studio pipeline`.
10
+
11
+ ## Install
12
+
13
+ Install the CLI and its pipeline-spec peer dependency in the repository that
14
+ owns your pipeline:
15
+
16
+ ```bash
17
+ pnpm add --save-dev \
18
+ @sequenceholdings/studio-cli \
19
+ @sequenceholdings/pipeline-spec
20
+ ```
21
+
22
+ ## Validate a pipeline repository
23
+
24
+ Run validation from the repository root:
25
+
26
+ ```bash
27
+ pnpm exec seq-studio pipeline validate .
28
+ ```
29
+
30
+ The validator discovers `*.stage.yml` files recursively. A stage specification
31
+ describes one deployable unit and its inputs, outputs, runtime, ownership, and
32
+ environment policy. For example:
33
+
34
+ ```yaml
35
+ schema_version: 1
36
+ stage: ingest-events
37
+ title: Event ingestion
38
+ description: Pulls event records into the bronze landing zone.
39
+ type: ingestion
40
+ owners:
41
+ - data-platform@example.com
42
+ criticality: standard
43
+ environments:
44
+ - staging
45
+ runtime: databricks
46
+ source:
47
+ system: events_api
48
+ credential: events_api_key
49
+ feeds:
50
+ events:
51
+ description: Event records updated since the previous run.
52
+ snapshot_mode: incremental
53
+ natural_key:
54
+ - event_id
55
+ cursor:
56
+ column: updated_at
57
+ format: json
58
+ schedule:
59
+ cron: "0 5 * * *"
60
+ tz: UTC
61
+ entrypoint: src/ingest_events.py
62
+ ```
63
+
64
+ Credentials are symbolic references such as `events_api_key`; never put secret
65
+ values, tokens, connection strings, or environment-specific URLs in a stage
66
+ specification.
67
+
68
+ ## Table and column descriptions
69
+
70
+ Keep bronze source documentation inline with each feed:
71
+
72
+ ```yaml
73
+ feeds:
74
+ events:
75
+ description: Event records updated since the previous run.
76
+ columns:
77
+ event_id:
78
+ description: Identifier assigned by the source system.
79
+ updated_at:
80
+ description: Time the source last updated this event.
81
+ ```
82
+
83
+ Each optional `columns` entry requires a `description` string and does not
84
+ accept types or constraints. The source determines bronze columns and types.
85
+ Description edits change specification provenance, not the data contract.
86
+ After deployment, `seq-studio pipeline schema <assetId> -e <env> --json`
87
+ returns descriptions alongside observed columns when supported by the server.
88
+
89
+ Stage YAML files allow up to 5 MiB; other specification files retain a 1 MiB
90
+ limit. Existing `column_descriptions` and `documentation_ref` declarations
91
+ remain supported; inline `columns.<name>.description` takes precedence.
92
+
93
+ ## Validation coverage
94
+
95
+ Offline validation checks:
96
+
97
+ - strict schemas for ingestion, transformation, and serving stages;
98
+ - references between stages, feeds, outputs, and triggers;
99
+ - asset column compatibility and contract changes;
100
+ - symbolic credential and webhook references;
101
+ - deterministic specification and contract hashes; and
102
+ - serving-stage compatibility with declared ORM bindings.
103
+
104
+ Validation reports all discoverable issues in one pass so repository authors
105
+ can correct the complete graph before deployment.
106
+
107
+ ## Compatibility
108
+
109
+ - Node.js 20 or newer
110
+ - package versioning follows the release consumed by `seq-studio`
111
+ - the root JavaScript export may change without a standalone public SDK
112
+ deprecation cycle
113
+
114
+ This package is distributed under the license declared in `package.json`.
@@ -0,0 +1,74 @@
1
+ import type { Criticality, IngestionStageSpec, StageSpecInput } from '../envelope.js';
2
+ import type { FeedFormat, FeedSpec, IngestionRuntime, ScheduleSpec, SnapshotMode } from '../bodies/ingestion.js';
3
+ /** Scalar extraction hints allowed on a feed (matches feedSchema.extraction). */
4
+ export type ExtractionHint = string | number | boolean;
5
+ /**
6
+ * One source table → one ingestion stage with a single feed. This is the
7
+ * recommended JH / SilverLake authoring shape (SEQ-2485): N sibling stages
8
+ * instead of N feeds in one stage.
9
+ */
10
+ export interface IngestionTableDefinition {
11
+ /** Source-side table identifier (e.g. iSeries DDMAST). */
12
+ table: string;
13
+ /** Override stage slug; default `${stagePrefix}-${tableSlug}`. */
14
+ stage?: string;
15
+ /** Override feed name; default is the lowercased table slug. */
16
+ feed?: string;
17
+ description?: string;
18
+ columns?: Readonly<Record<string, {
19
+ readonly description: string;
20
+ }>>;
21
+ column_descriptions?: Readonly<Record<string, string>>;
22
+ documentation_ref?: string;
23
+ snapshot_mode: SnapshotMode;
24
+ natural_key: readonly string[];
25
+ cursor?: {
26
+ column: string;
27
+ encoding?: string;
28
+ };
29
+ format?: FeedFormat;
30
+ pii?: FeedSpec['pii'];
31
+ extraction?: Readonly<Record<string, ExtractionHint>>;
32
+ }
33
+ /** Shared envelope + runtime config replicated onto every generated stage. */
34
+ export interface PerTableIngestionAuthoring {
35
+ /** Prefix for generated stage slugs (`silverlake` → `silverlake-ddmast`). */
36
+ stagePrefix: string;
37
+ runtime: IngestionRuntime;
38
+ entrypoint: string;
39
+ owners: readonly string[];
40
+ criticality: Criticality;
41
+ environments: readonly string[];
42
+ /** Applied to every stage unless overridden per table. */
43
+ titleTemplate?: string;
44
+ descriptionTemplate?: string;
45
+ slos?: IngestionStageSpec['slos'];
46
+ alerts?: IngestionStageSpec['alerts'];
47
+ runbook?: string;
48
+ remediation_posture?: IngestionStageSpec['remediation_posture'];
49
+ worker?: IngestionStageSpec['worker'];
50
+ source?: IngestionStageSpec['source'];
51
+ schedule?: ScheduleSpec;
52
+ freshness_slo?: string;
53
+ retention?: string;
54
+ tables: readonly IngestionTableDefinition[];
55
+ }
56
+ export interface GeneratedStageFile {
57
+ readonly stage: string;
58
+ readonly filename: string;
59
+ readonly spec: StageSpecInput;
60
+ readonly yaml: string;
61
+ }
62
+ /** Lowercase identifier from a source table name (DDMAST → ddmast). */
63
+ export declare function tableToSlug(table: string): string;
64
+ /** Kebab-case stage slug from prefix + table (`silverlake`, `DDMAST` → `silverlake-ddmast`). */
65
+ export declare function perTableStageSlug(stagePrefix: string, table: string): string;
66
+ /**
67
+ * Expand a typed authoring definition into one validated-shaped stage spec per
68
+ * table. Does not run Zod parse — callers should `stageSpecSchema.parse` each
69
+ * spec (or run `seq-studio pipeline validate` on the emitted directory).
70
+ */
71
+ export declare function buildPerTableIngestionStages(authoring: PerTableIngestionAuthoring): readonly StageSpecInput[];
72
+ /** Build stage specs and their on-disk filenames (`<stage>.stage.yml`). */
73
+ export declare function generatePerTableStageFiles(authoring: PerTableIngestionAuthoring): readonly GeneratedStageFile[];
74
+ //# sourceMappingURL=per-table-ingestion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-table-ingestion.d.ts","sourceRoot":"","sources":["../../src/authoring/per-table-ingestion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { workerCapacityGroup } from '../bodies/ingestion.js';
2
+ import { dumpStageYaml } from './yaml.js';
3
+ const TABLE_SLUG_PATTERN = /[^a-z0-9]+/g;
4
+ /** Lowercase identifier from a source table name (DDMAST → ddmast). */
5
+ export function tableToSlug(table) {
6
+ const slug = table.trim().toLowerCase().replace(TABLE_SLUG_PATTERN, '_').replace(/^_|_$/g, '');
7
+ if (!slug)
8
+ throw new Error(`table '${table}' does not yield a valid slug`);
9
+ return slug;
10
+ }
11
+ /** Kebab-case stage slug from prefix + table (`silverlake`, `DDMAST` → `silverlake-ddmast`). */
12
+ export function perTableStageSlug(stagePrefix, table) {
13
+ return `${stagePrefix}-${tableToSlug(table)}`;
14
+ }
15
+ function renderTemplate(template, table, feed) {
16
+ return template.replace(/\{\{table\}\}/g, table).replace(/\{\{feed\}\}/g, feed);
17
+ }
18
+ function buildFeed(table) {
19
+ const feedName = table.feed ?? tableToSlug(table.table);
20
+ const extraction = {
21
+ table: table.table,
22
+ ...table.extraction,
23
+ };
24
+ const feed = {
25
+ snapshot_mode: table.snapshot_mode,
26
+ natural_key: [...table.natural_key],
27
+ pii: table.pii ?? 'none',
28
+ format: table.format ?? 'parquet',
29
+ extraction,
30
+ };
31
+ if (table.description)
32
+ feed.description = table.description;
33
+ if (table.columns)
34
+ feed.columns = Object.fromEntries(Object.entries(table.columns).map(([name, column]) => [name, { ...column }]));
35
+ if (table.column_descriptions)
36
+ feed.column_descriptions = { ...table.column_descriptions };
37
+ if (table.documentation_ref)
38
+ feed.documentation_ref = table.documentation_ref;
39
+ if (table.cursor)
40
+ feed.cursor = { ...table.cursor };
41
+ return { [feedName]: feed };
42
+ }
43
+ /**
44
+ * The group N sibling stages contend on. Grouping by the shared upstream — the
45
+ * worker machine + data source for edge workers, the source system for API
46
+ * pulls — is what keeps ~150 per-table stages from hammering one ODBC
47
+ * connection or one rate-limited API the moment their crons align. A per-stage
48
+ * group would be a declaration that they are independent, which they are not.
49
+ * Falls back to the stage slug only when a stage has no shared upstream to name.
50
+ */
51
+ function capacityGroup(authoring, stage) {
52
+ if (authoring.worker)
53
+ return workerCapacityGroup(authoring.worker);
54
+ if (authoring.source)
55
+ return authoring.source.system;
56
+ return stage.replace(/-/g, '_');
57
+ }
58
+ function buildSchedule(schedule, group) {
59
+ if (!schedule)
60
+ return undefined;
61
+ const policy = schedule.policy;
62
+ return {
63
+ ...schedule,
64
+ // An explicitly authored group always wins — coordinated maintenance
65
+ // windows and deliberately isolated tables both need the override.
66
+ policy: policy
67
+ ? { ...policy, exclusion_group: policy.exclusion_group ?? group }
68
+ : { singleton: true, exclusion_group: group },
69
+ };
70
+ }
71
+ /**
72
+ * Expand a typed authoring definition into one validated-shaped stage spec per
73
+ * table. Does not run Zod parse — callers should `stageSpecSchema.parse` each
74
+ * spec (or run `seq-studio pipeline validate` on the emitted directory).
75
+ */
76
+ export function buildPerTableIngestionStages(authoring) {
77
+ if (authoring.tables.length === 0) {
78
+ throw new Error('buildPerTableIngestionStages: tables must be non-empty');
79
+ }
80
+ const titleTemplate = authoring.titleTemplate ?? '{{table}} extraction';
81
+ const descriptionTemplate = authoring.descriptionTemplate ?? 'Ingest {{table}} into bronze via {{feed}}.';
82
+ return authoring.tables.map((table) => {
83
+ const stage = table.stage ?? perTableStageSlug(authoring.stagePrefix, table.table);
84
+ const feedName = table.feed ?? tableToSlug(table.table);
85
+ return {
86
+ schema_version: 1,
87
+ stage,
88
+ title: renderTemplate(titleTemplate, table.table, feedName),
89
+ description: renderTemplate(descriptionTemplate, table.table, feedName),
90
+ type: 'ingestion',
91
+ owners: [...authoring.owners],
92
+ criticality: authoring.criticality,
93
+ ...(authoring.slos ? { slos: authoring.slos } : {}),
94
+ ...(authoring.alerts ? { alerts: authoring.alerts } : {}),
95
+ ...(authoring.runbook ? { runbook: authoring.runbook } : {}),
96
+ ...(authoring.remediation_posture
97
+ ? { remediation_posture: authoring.remediation_posture }
98
+ : {}),
99
+ environments: [...authoring.environments],
100
+ runtime: authoring.runtime,
101
+ ...(authoring.worker ? { worker: authoring.worker } : {}),
102
+ ...(authoring.source ? { source: authoring.source } : {}),
103
+ feeds: buildFeed(table),
104
+ ...(authoring.schedule
105
+ ? { schedule: buildSchedule(authoring.schedule, capacityGroup(authoring, stage)) }
106
+ : {}),
107
+ ...(authoring.freshness_slo ? { freshness_slo: authoring.freshness_slo } : {}),
108
+ ...(authoring.retention ? { retention: authoring.retention } : {}),
109
+ entrypoint: authoring.entrypoint,
110
+ };
111
+ });
112
+ }
113
+ /** Build stage specs and their on-disk filenames (`<stage>.stage.yml`). */
114
+ export function generatePerTableStageFiles(authoring) {
115
+ return buildPerTableIngestionStages(authoring).map((spec) => ({
116
+ stage: spec.stage,
117
+ filename: `${spec.stage}.stage.yml`,
118
+ spec,
119
+ yaml: dumpStageYaml(spec),
120
+ }));
121
+ }
122
+ //# sourceMappingURL=per-table-ingestion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-table-ingestion.js","sourceRoot":"","sources":["../../src/authoring/per-table-ingestion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deterministic YAML for generated stage specs — sorted keys, stable quoting.
3
+ * Same spec object → byte-identical output (golden-friendly).
4
+ */
5
+ export declare function dumpStageYaml(value: unknown): string;
6
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/authoring/yaml.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQpD"}
@@ -0,0 +1,15 @@
1
+ import { dump } from 'js-yaml';
2
+ /**
3
+ * Deterministic YAML for generated stage specs — sorted keys, stable quoting.
4
+ * Same spec object → byte-identical output (golden-friendly).
5
+ */
6
+ export function dumpStageYaml(value) {
7
+ return dump(value, {
8
+ sortKeys: true,
9
+ lineWidth: 120,
10
+ noRefs: true,
11
+ quotingType: '"',
12
+ forceQuotes: false,
13
+ });
14
+ }
15
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/authoring/yaml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,IAAI,CAAC,KAAK,EAAE;QACjB,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,GAAG;QAChB,WAAW,EAAE,KAAK;KACnB,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { ResolutionErrorCode } from './errors.js';
2
+ import type { StageSpec } from './envelope.js';
3
+ import { type TargetFactsInput } from './resolve.js';
4
+ export interface BindingGap {
5
+ readonly code: ResolutionErrorCode;
6
+ readonly message: string;
7
+ readonly symbolic?: string;
8
+ readonly stage?: string;
9
+ }
10
+ /**
11
+ * Collect target-binding gaps for one portable spec without throwing.
12
+ * `resolveSpec` remains fail-fast for execute paths; plan aggregates via
13
+ * {@link collectPipelineBindingGaps}.
14
+ */
15
+ export declare function collectBindingGaps(spec: StageSpec, facts: TargetFactsInput): BindingGap[];
16
+ /** Aggregate binding gaps across every stage spec in a deploy unit. */
17
+ export declare function collectPipelineBindingGaps({ specs, facts, }: {
18
+ specs: readonly StageSpec[];
19
+ facts: TargetFactsInput;
20
+ }): BindingGap[];
21
+ //# sourceMappingURL=binding-gaps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-gaps.d.ts","sourceRoot":"","sources":["../src/binding-gaps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,GAAG,UAAU,EAAE,CAoEzF;AAED,uEAAuE;AACvE,wBAAgB,0BAA0B,CAAC,EACzC,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;IAC3B,KAAK,EAAE,gBAAgB,CAAA;CACxB,GAAG,UAAU,EAAE,CAEf"}
@@ -0,0 +1,74 @@
1
+ import { targetFactsSchema } from './resolve.js';
2
+ /**
3
+ * Collect target-binding gaps for one portable spec without throwing.
4
+ * `resolveSpec` remains fail-fast for execute paths; plan aggregates via
5
+ * {@link collectPipelineBindingGaps}.
6
+ */
7
+ export function collectBindingGaps(spec, facts) {
8
+ const parsedFacts = targetFactsSchema.safeParse(facts);
9
+ if (!parsedFacts.success) {
10
+ return [
11
+ {
12
+ code: 'invalid_target_facts',
13
+ message: `invalid target facts: ${parsedFacts.error.issues
14
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
15
+ .join('; ')}`,
16
+ stage: spec.stage,
17
+ },
18
+ ];
19
+ }
20
+ const target = parsedFacts.data;
21
+ if (!spec.environments.includes(target.deploy_target)) {
22
+ return [
23
+ {
24
+ code: 'unknown_target',
25
+ message: `stage '${spec.stage}' does not declare environment '${target.deploy_target}' (declared: ${spec.environments.join(', ')})`,
26
+ symbolic: target.deploy_target,
27
+ stage: spec.stage,
28
+ },
29
+ ];
30
+ }
31
+ const gaps = [];
32
+ if (!target.workspace_id) {
33
+ gaps.push({
34
+ code: 'missing_workspace_binding',
35
+ message: `target '${target.deploy_target}' has no workspace binding — every deploy target must bind exactly one workspace_id (no fallback)`,
36
+ symbolic: target.deploy_target,
37
+ stage: spec.stage,
38
+ });
39
+ }
40
+ if (spec.type === 'ingestion' && spec.source?.credential) {
41
+ if (!target.credential_bindings[spec.source.credential]) {
42
+ gaps.push({
43
+ code: 'missing_credential_binding',
44
+ message: `target '${target.deploy_target}' has no credential binding for symbolic name '${spec.source.credential}'`,
45
+ symbolic: spec.source.credential,
46
+ stage: spec.stage,
47
+ });
48
+ }
49
+ }
50
+ if (spec.type === 'serving' && spec.transport?.kind === 'postgres_snapshot_landing_v1') {
51
+ const binding = target.postgres_snapshot_bindings?.[spec.transport.binding];
52
+ if (!binding || binding.database !== spec.transport.database)
53
+ gaps.push({
54
+ code: 'invalid_target_facts', stage: spec.stage, symbolic: spec.transport.binding,
55
+ message: `snapshot transport binding '${spec.transport.binding}' is missing or names a different database`,
56
+ });
57
+ }
58
+ for (const channel of spec.alerts?.channels ?? []) {
59
+ if (!target.alert_channel_bindings[channel]) {
60
+ gaps.push({
61
+ code: 'missing_alert_channel_binding',
62
+ message: `target '${target.deploy_target}' has no alert channel binding for symbolic name '${channel}'`,
63
+ symbolic: channel,
64
+ stage: spec.stage,
65
+ });
66
+ }
67
+ }
68
+ return gaps;
69
+ }
70
+ /** Aggregate binding gaps across every stage spec in a deploy unit. */
71
+ export function collectPipelineBindingGaps({ specs, facts, }) {
72
+ return specs.flatMap((spec) => collectBindingGaps(spec, facts));
73
+ }
74
+ //# sourceMappingURL=binding-gaps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-gaps.js","sourceRoot":"","sources":["../src/binding-gaps.ts"],"names":[],"mappings":"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"}