@veltrixsecops/app-sdk 3.2.0 → 3.5.1

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 (77) hide show
  1. package/README.md +68 -1
  2. package/dist/byol/index.cjs +3802 -0
  3. package/dist/byol/index.cjs.map +1 -0
  4. package/dist/byol/index.d.cts +578 -0
  5. package/dist/byol/index.d.ts +578 -0
  6. package/dist/byol/index.js +2587 -0
  7. package/dist/byol/index.js.map +1 -0
  8. package/dist/chunk-3WK4EVQ3.js +842 -0
  9. package/dist/chunk-3WK4EVQ3.js.map +1 -0
  10. package/dist/{chunk-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
  11. package/dist/chunk-HIL6RIDG.js.map +1 -0
  12. package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
  13. package/dist/chunk-Y6XBNZGR.js.map +1 -0
  14. package/dist/client/index.cjs +112 -2
  15. package/dist/client/index.cjs.map +1 -1
  16. package/dist/client/index.d.cts +118 -6
  17. package/dist/client/index.d.ts +118 -6
  18. package/dist/client/index.js +13 -1
  19. package/dist/connections/index.cjs +1787 -0
  20. package/dist/connections/index.cjs.map +1 -0
  21. package/dist/connections/index.d.cts +78 -0
  22. package/dist/connections/index.d.ts +78 -0
  23. package/dist/connections/index.js +754 -0
  24. package/dist/connections/index.js.map +1 -0
  25. package/dist/hooks/index.cjs.map +1 -1
  26. package/dist/hooks/index.d.cts +2 -2
  27. package/dist/hooks/index.d.ts +2 -2
  28. package/dist/hooks/index.js +1 -1
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +3 -3
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +3 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/opentofu/index.cjs +138 -0
  36. package/dist/opentofu/index.cjs.map +1 -0
  37. package/dist/opentofu/index.d.cts +131 -0
  38. package/dist/opentofu/index.d.ts +131 -0
  39. package/dist/opentofu/index.js +109 -0
  40. package/dist/opentofu/index.js.map +1 -0
  41. package/dist/pipeline/index.cjs +7 -0
  42. package/dist/pipeline/index.cjs.map +1 -1
  43. package/dist/pipeline/index.d.cts +9 -2
  44. package/dist/pipeline/index.d.ts +9 -2
  45. package/dist/pipeline/index.js +3 -1
  46. package/dist/pipeline-beeT8dje.d.cts +408 -0
  47. package/dist/pipeline-beeT8dje.d.ts +408 -0
  48. package/dist/ui/index.cjs +136 -0
  49. package/dist/ui/index.cjs.map +1 -1
  50. package/dist/ui/index.d.cts +109 -1
  51. package/dist/ui/index.d.ts +109 -1
  52. package/dist/ui/index.js +35 -677
  53. package/dist/ui/index.js.map +1 -1
  54. package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
  55. package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
  56. package/opentofu/README.md +114 -0
  57. package/opentofu/modules/aws/main.tf +1008 -0
  58. package/opentofu/modules/aws/outputs.tf +90 -0
  59. package/opentofu/modules/aws/variables.tf +437 -0
  60. package/opentofu/modules/aws/versions.tf +23 -0
  61. package/opentofu/modules/azure/main.tf +920 -0
  62. package/opentofu/modules/azure/outputs.tf +72 -0
  63. package/opentofu/modules/azure/variables.tf +498 -0
  64. package/opentofu/modules/azure/versions.tf +27 -0
  65. package/opentofu/modules/gcp/main.tf +645 -0
  66. package/opentofu/modules/gcp/outputs.tf +73 -0
  67. package/opentofu/modules/gcp/variables.tf +428 -0
  68. package/opentofu/modules/gcp/versions.tf +27 -0
  69. package/opentofu/modules/hetzner/main.tf +432 -0
  70. package/opentofu/modules/hetzner/outputs.tf +72 -0
  71. package/opentofu/modules/hetzner/variables.tf +423 -0
  72. package/opentofu/modules/hetzner/versions.tf +26 -0
  73. package/package.json +33 -1
  74. package/dist/chunk-EOOEHZGC.js.map +0 -1
  75. package/dist/chunk-VWFTOFTI.js.map +0 -1
  76. package/dist/pipeline-A-gSsPRR.d.cts +0 -189
  77. package/dist/pipeline-A-gSsPRR.d.ts +0 -189
@@ -0,0 +1,423 @@
1
+ # =============================================================================
2
+ # Hetzner Cloud environment module — input variables.
3
+ #
4
+ # A faithful, tool-agnostic translation of the AWS reference module
5
+ # (sdk/opentofu/modules/aws). It honors the SAME variable + output contract:
6
+ # the spec-derived block (foundation_kinds, compute_kinds, security_rules,
7
+ # load_balancer, dns_prefixes, waf_enabled, alb_auth) is byte-identical to AWS
8
+ # so `renderInfraVars` output feeds every cloud unchanged; the deployment vars
9
+ # keep the AWS names and adapt only the Hetzner specifics (server_type, image,
10
+ # ssh_keys, network zone/location).
11
+ #
12
+ # One BYOL environment (a "stack") = one per-stack cloud subnet on a Hetzner
13
+ # private network, plus one hcloud_server per compute plan item and the
14
+ # storage / TLS / LB a tier needs. See the gap notes in main.tf for the honest
15
+ # handling of Hetzner's missing primitives (WAF, in-provider DNS, object store,
16
+ # secret store, LB-level MFA).
17
+ # =============================================================================
18
+
19
+ # --- Identity / naming / tenancy ------------------------------------------
20
+
21
+ variable "app_id" {
22
+ description = "Owning app id, e.g. my-app. Used for naming + state key."
23
+ type = string
24
+ }
25
+
26
+ variable "customer_id" {
27
+ description = "Tenant (customer) id. Used for naming + label Veltrix_Customer."
28
+ type = string
29
+ }
30
+
31
+ variable "infrastructure_id" {
32
+ description = "BYOL infrastructure (environment/stack) id. Unique per stack."
33
+ type = string
34
+ }
35
+
36
+ variable "provider_code" {
37
+ description = "Cloud provider code (informational; this module is Hetzner). One of aws|azure|gcp|hetzner."
38
+ type = string
39
+ default = "hetzner"
40
+ }
41
+
42
+ variable "region" {
43
+ description = <<-EOT
44
+ Hetzner target region. Accepts EITHER a location (e.g. nbg1, fsn1, hel1,
45
+ ash, hil, sin) OR a network zone (e.g. eu-central, us-east, us-west,
46
+ ap-southeast). Servers/volumes/LB use the location; the private network
47
+ subnet + LB use the network zone. When a location is given the zone is
48
+ derived (and vice-versa); override explicitly with `location` / `network_zone`.
49
+ EOT
50
+ type = string
51
+ }
52
+
53
+ variable "location" {
54
+ description = "Explicit Hetzner location for servers/volumes (e.g. nbg1). Empty = derive from region."
55
+ type = string
56
+ default = ""
57
+ }
58
+
59
+ variable "network_zone" {
60
+ description = "Explicit Hetzner network zone for the subnet + LB (e.g. eu-central). Empty = derive from region."
61
+ type = string
62
+ default = ""
63
+ }
64
+
65
+ # --- Network (mode: hosted-shared vs BYOC dedicated/existing) --------------
66
+ # network_mode is a DEPLOYMENT-TARGET var set by the worker per environment — it
67
+ # is NOT part of the app's InfraSpec (which describes only the tool). One app
68
+ # spec deploys hosted OR into a customer's own account, any cloud.
69
+
70
+ variable "network_mode" {
71
+ description = <<-EOT
72
+ How the environment's network is sourced:
73
+ shared — Veltrix-hosted: data-source the shared hcloud_network
74
+ (network_ref) and create the per-stack cloud subnet (subnet_cidr).
75
+ dedicated — BYOC: CREATE a fresh hcloud_network (vpc_cidr) + one cloud
76
+ subnet (subnet_cidr, or the whole ip_range), isolated per env.
77
+ existing — BYOC: data-source a customer-designated hcloud_network
78
+ (network_ref) and create a subnet inside it (subnet_cidr).
79
+ EOT
80
+ type = string
81
+ default = "shared"
82
+
83
+ validation {
84
+ condition = contains(["shared", "dedicated", "existing"], var.network_mode)
85
+ error_message = "network_mode must be one of: shared, dedicated, existing."
86
+ }
87
+ }
88
+
89
+ variable "network_ref" {
90
+ description = <<-EOT
91
+ Reference to the hcloud_network to deploy into, for network_mode =
92
+ shared|existing. Resolved per network_lookup_by (name | id | label selector).
93
+ Ignored for network_mode = dedicated (the network is created).
94
+ EOT
95
+ type = string
96
+ default = ""
97
+ }
98
+
99
+ variable "network_lookup_by" {
100
+ description = <<-EOT
101
+ How to resolve network_ref for shared|existing:
102
+ name — the hcloud_network name (default),
103
+ id — the numeric hcloud_network id,
104
+ tag — a Hetzner label selector (e.g. "veltrix-shared==true"), via
105
+ the hcloud_network data source `with_selector`.
106
+ ("tag" is kept for cross-cloud name parity with the AWS module, mapped to
107
+ Hetzner's label selector — Hetzner networks carry labels, not tags.)
108
+ EOT
109
+ type = string
110
+ default = "name"
111
+
112
+ validation {
113
+ condition = contains(["name", "id", "tag"], var.network_lookup_by)
114
+ error_message = "network_lookup_by must be one of: name, id, tag."
115
+ }
116
+ }
117
+
118
+ variable "subnet_cidr" {
119
+ description = <<-EOT
120
+ Per-stack cloud subnet ip_range for network_mode = shared|existing (and, when
121
+ set, for dedicated). Empty is allowed only when dedicated (the whole vpc_cidr
122
+ is then used as a single subnet).
123
+ EOT
124
+ type = string
125
+ default = ""
126
+
127
+ validation {
128
+ condition = var.subnet_cidr == "" || can(cidrhost(var.subnet_cidr, 0))
129
+ error_message = "subnet_cidr must be empty or a valid IPv4 CIDR, e.g. 10.20.16.0/24."
130
+ }
131
+ }
132
+
133
+ variable "vpc_cidr" {
134
+ description = <<-EOT
135
+ ip_range for the hcloud_network CREATED when network_mode = dedicated (BYOC).
136
+ A single cloud subnet is carved from it (subnet_cidr, or the whole range).
137
+ Ignored otherwise. Named `vpc_cidr` for cross-cloud contract parity.
138
+ EOT
139
+ type = string
140
+ default = "10.60.0.0/16"
141
+
142
+ validation {
143
+ condition = can(cidrhost(var.vpc_cidr, 0))
144
+ error_message = "vpc_cidr must be a valid IPv4 CIDR, e.g. 10.60.0.0/16."
145
+ }
146
+ }
147
+
148
+ variable "admin_cidr" {
149
+ description = "CIDR allowed to reach management ports + the web UI when there is no LB. Typically the Veltrix control-plane / customer bastion range."
150
+ type = string
151
+ default = "10.0.0.0/8"
152
+ }
153
+
154
+ # --- The plan (topology) --------------------------------------------------
155
+
156
+ variable "plan" {
157
+ description = <<-EOT
158
+ Ordered resource plan from the app topology. One object per resource the
159
+ environment needs. `plan_key` is the stable key that maps 1:1 to a
160
+ BYOL-resource row; the CI apply emits resource.status per plan_key.
161
+ EOT
162
+ type = list(object({
163
+ plan_key = string
164
+ tier = string # foundation | control-plane | data | search | ingest
165
+ kind = string # network | storage | indexer | search-head | management-node | ...
166
+ name = optional(string, "")
167
+ role = optional(string, "")
168
+ region = optional(string, null)
169
+ # Accepted for cross-cloud plan-shape parity. Hetzner has no in-location AZs,
170
+ # so `zone` is unused here (multi-site uses locations via region granularity);
171
+ # `roles` carries a consolidated control-plane node's role set for bring-up.
172
+ zone = optional(string, null)
173
+ roles = optional(list(string), [])
174
+ }))
175
+
176
+ validation {
177
+ condition = length(var.plan) > 0
178
+ error_message = "plan must contain at least one item."
179
+ }
180
+ }
181
+
182
+ # --- Per-tier compute sizing ----------------------------------------------
183
+ # Mirrors the AWS instance_types / instance_types_by_kind / default_instance_type
184
+ # trio, keyed the same way. Hetzner has no separately-sized root disk: a server
185
+ # type fixes its own root disk, so there is no `root_volume_gb` equivalent
186
+ # (extra capacity is a block volume — see storage_volume_gb).
187
+
188
+ variable "server_types" {
189
+ description = <<-EOT
190
+ Per-tier hcloud server_type override, keyed by tier
191
+ (foundation|control-plane|data|search|ingest). Missing tiers fall back to
192
+ default_server_type. Kind-level overrides go in server_types_by_kind.
193
+ EOT
194
+ type = map(string)
195
+ default = {}
196
+ }
197
+
198
+ variable "server_types_by_kind" {
199
+ description = "Per-kind hcloud server_type override (wins over server_types). e.g. { indexer = \"ccx33\" }."
200
+ type = map(string)
201
+ default = {}
202
+ }
203
+
204
+ variable "default_server_type" {
205
+ description = "Fallback hcloud server_type for any compute plan item with no tier/kind override (e.g. cx22, cpx31, ccx23)."
206
+ type = string
207
+ default = "cx22"
208
+ }
209
+
210
+ variable "storage_volume_gb" {
211
+ description = "Size (GiB) of the hcloud_volume created per `storage` plan item. Min 10. See the object-storage gap note in main.tf."
212
+ type = number
213
+ default = 100
214
+ }
215
+
216
+ # --- Machine image / access -----------------------------------------------
217
+
218
+ variable "image" {
219
+ description = <<-EOT
220
+ hcloud image for compute nodes — a system image name (e.g. ubuntu-22.04,
221
+ debian-12) or a snapshot id. Defaults to ubuntu-22.04 (scaffold only);
222
+ production SHOULD supply a hardened, tool-preinstalled snapshot id.
223
+ EOT
224
+ type = string
225
+ default = "ubuntu-22.04"
226
+ }
227
+
228
+ variable "ssh_keys" {
229
+ description = "Optional list of hcloud SSH key names or ids to inject for break-glass access. Empty = none."
230
+ type = list(string)
231
+ default = []
232
+ }
233
+
234
+ # --- Foundation options ----------------------------------------------------
235
+
236
+ variable "dns_domain" {
237
+ description = "Base domain for the environment (e.g. <cust>-<env>.veltrixsecops.com, or a customer domain in BYOC). Drives per-node FQDNs (emitted as output). Required only if the plan includes a dns item."
238
+ type = string
239
+ default = ""
240
+ }
241
+
242
+ variable "dns_mode" {
243
+ description = <<-EOT
244
+ How the PUBLIC (analyst-facing) DNS name + TLS cert are handled. NOTE: on
245
+ Hetzner this module creates NO DNS records in any mode — the hcloud provider
246
+ has no managed-DNS resource (Hetzner DNS is the separate `timohirt/hetznerdns`
247
+ provider). The variable is kept for cross-cloud contract parity; per-node
248
+ FQDNs are emitted as `node_fqdns` for the out-of-band bring-up layer.
249
+ managed — public name + cert handled out-of-band; the LB serves HTTPS
250
+ using certificate_arn when provided.
251
+ delegated — the worker manages the public record cross-account; the LB
252
+ uses certificate_arn on its HTTPS service.
253
+ private-only — no public DNS; reached via the customer network (ZTNA/VPN).
254
+ EOT
255
+ type = string
256
+ default = "managed"
257
+
258
+ validation {
259
+ condition = contains(["managed", "delegated", "private-only"], var.dns_mode)
260
+ error_message = "dns_mode must be one of: managed, delegated, private-only."
261
+ }
262
+ }
263
+
264
+ variable "certificate_arn" {
265
+ description = <<-EOT
266
+ hcloud Certificate id for the LB's HTTPS service (an UPLOADED or MANAGED
267
+ hcloud_certificate provisioned out-of-band — a managed cert needs Hetzner
268
+ DNS, which this module does not touch). Must be numeric (the hcloud cert id).
269
+ Empty => the LB serves plain HTTP on the listener port (documented). Named
270
+ `certificate_arn` for cross-cloud contract parity; it is NOT an AWS ARN.
271
+ EOT
272
+ type = string
273
+ default = ""
274
+ }
275
+
276
+ variable "create_private_zone" {
277
+ description = <<-EOT
278
+ Cross-cloud parity flag. On AWS this creates a Route53 PRIVATE zone for
279
+ per-node FQDNs. Hetzner has no in-provider DNS, so this is a DOCUMENTED
280
+ NO-OP: no zone/record is created. Per-node FQDNs are still emitted as
281
+ `node_fqdns` for the bring-up layer (hetznerdns provider or /etc/hosts).
282
+ EOT
283
+ type = bool
284
+ default = false
285
+ }
286
+
287
+ variable "private_zone_id" {
288
+ description = <<-EOT
289
+ Cross-cloud parity input. On AWS this is an existing Route53 private zone id.
290
+ Hetzner has no in-provider DNS, so it is UNUSED here (documented no-op); kept
291
+ so the worker can pass a uniform variable set across clouds.
292
+ EOT
293
+ type = string
294
+ default = ""
295
+ }
296
+
297
+ # --- Declarative infra spec (rendered from the app's InfraSpec) -----------
298
+ # These are BYTE-IDENTICAL to the AWS module (same names, types, defaults,
299
+ # validations) so `renderInfraVars` (sdk/src/opentofu/render.ts) feeds every
300
+ # cloud module unchanged. NOTHING below is app-specific or cloud-specific.
301
+
302
+ variable "foundation_kinds" {
303
+ description = <<-EOT
304
+ Plan `kind`s the module realizes as shared FOUNDATION infra (not compute).
305
+ Any plan item whose kind is NOT in this set (and not named by compute_kinds)
306
+ is a compute node. Kept in sync with FOUNDATION_KINDS in spec.ts.
307
+ EOT
308
+ type = list(string)
309
+ default = [
310
+ "network", "storage", "secrets", "tls",
311
+ "load-balancer", "dns", "license-file", "hec",
312
+ ]
313
+ }
314
+
315
+ variable "compute_kinds" {
316
+ description = <<-EOT
317
+ Optional explicit allow-list of compute kinds. When non-empty, ONLY these
318
+ kinds become compute nodes. When empty (default), compute = any plan item
319
+ whose kind is not in foundation_kinds (so an app's roles are compute
320
+ automatically).
321
+ EOT
322
+ type = list(string)
323
+ default = []
324
+ }
325
+
326
+ variable "security_rules" {
327
+ description = <<-EOT
328
+ SG ingress rules from the app's InfraSpec. Each rule opens `port` to one or
329
+ more `sources`: "self" (peer nodes — the node SG referencing itself),
330
+ "alb" (the public ALB SG; ignored when there is no load balancer), or
331
+ "admin" (var.admin_cidr). Replaces any hardcoded, tool-specific port list.
332
+ EOT
333
+ type = list(object({
334
+ port = number
335
+ protocol = optional(string, "tcp")
336
+ sources = list(string)
337
+ description = optional(string, "")
338
+ }))
339
+ default = []
340
+
341
+ validation {
342
+ condition = alltrue([
343
+ for r in var.security_rules : alltrue([
344
+ for s in r.sources : contains(["self", "alb", "admin"], s)
345
+ ])
346
+ ])
347
+ error_message = "Every security_rules[*].sources entry must be one of: self, alb, admin."
348
+ }
349
+ }
350
+
351
+ variable "load_balancer" {
352
+ description = <<-EOT
353
+ Front-door ALB spec from the app's InfraSpec. Null for headless / forwarder-
354
+ only tools. When set (and the plan carries a load-balancer item), the module
355
+ builds the target group + health check + listeners from this. `target_kinds`
356
+ are the compute kinds that sit behind the ALB.
357
+ EOT
358
+ type = object({
359
+ target_port = number
360
+ target_protocol = optional(string, "HTTP")
361
+ health_check_path = string
362
+ health_check_matcher = optional(string, "200-399")
363
+ health_check_protocol = optional(string, "")
364
+ target_kinds = list(string)
365
+ listener_port = optional(number, 443)
366
+ })
367
+ default = null
368
+ }
369
+
370
+ variable "dns_prefixes" {
371
+ description = <<-EOT
372
+ kind -> DNS label prefix for per-node function FQDNs (e.g.
373
+ { indexer = "idx", search-head = "sh", cluster-manager = "mgmt" }). A compute
374
+ kind absent from the map falls back to the kind string itself.
375
+ EOT
376
+ type = map(string)
377
+ default = {}
378
+ }
379
+
380
+ variable "waf_enabled" {
381
+ description = "Attach a WAFv2 web ACL (managed rules + IP rate limit) to the ALB. Ignored when there is no load balancer."
382
+ type = bool
383
+ default = true
384
+ }
385
+
386
+ variable "alb_auth" {
387
+ description = <<-EOT
388
+ Optional OIDC/Cognito MFA enforced at the ALB, in front of the app's web UI. When
389
+ enabled, the HTTPS listener authenticates against the given Cognito user pool
390
+ BEFORE forwarding to the search target group. Leave disabled (default) for a
391
+ v1 public ALB + WAF posture without ALB-level auth. When enabled, all three
392
+ user_pool_* fields are required.
393
+ EOT
394
+ type = object({
395
+ enabled = optional(bool, false)
396
+ user_pool_arn = optional(string, "")
397
+ user_pool_client_id = optional(string, "")
398
+ user_pool_domain = optional(string, "")
399
+ })
400
+ default = {}
401
+
402
+ validation {
403
+ condition = !var.alb_auth.enabled || (
404
+ var.alb_auth.user_pool_arn != "" &&
405
+ var.alb_auth.user_pool_client_id != "" &&
406
+ var.alb_auth.user_pool_domain != ""
407
+ )
408
+ error_message = "When alb_auth.enabled is true, user_pool_arn, user_pool_client_id and user_pool_domain are all required."
409
+ }
410
+ }
411
+
412
+ # --- Tags -> Hetzner labels (threaded onto every labelable resource) -------
413
+
414
+ variable "tags" {
415
+ description = <<-EOT
416
+ Canonical Veltrix tag set. On Hetzner these are SANITIZED into labels
417
+ (colons and other invalid characters replaced with "_", truncated to 63)
418
+ because hcloud labels use a restricted charset. The OIDC/token apply
419
+ identity is scoped by Veltrix:ManagedBy = Veltrix, so this map MUST include
420
+ it (it becomes the Veltrix_ManagedBy label).
421
+ EOT
422
+ type = map(string)
423
+ }
@@ -0,0 +1,26 @@
1
+ # =============================================================================
2
+ # Hetzner Cloud environment module — provider/version pinning (OpenTofu HCL).
3
+ #
4
+ # OpenTofu resolves providers from registry.opentofu.org by default; the
5
+ # `hetznercloud/hcloud` source is mirrored there, so this block is compatible
6
+ # with both `tofu` and `terraform`. We pin OpenTofu >= 1.6.0 (the first stable
7
+ # OpenTofu line). No `random` provider is needed — Hetzner resource names are
8
+ # project-scoped (not globally unique like an S3 bucket), so the deterministic
9
+ # name_prefix (app_id + infrastructure_id) is sufficient for uniqueness.
10
+ #
11
+ # NOTE: Hetzner DNS is a SEPARATE product with a SEPARATE provider
12
+ # (`timohirt/hetznerdns`). It is deliberately NOT declared here — this module
13
+ # creates no DNS records (see the DNS gap note in main.tf). Per-node FQDNs are
14
+ # emitted as an output for the out-of-band bring-up layer to consume.
15
+ # =============================================================================
16
+
17
+ terraform {
18
+ required_version = ">= 1.6.0"
19
+
20
+ required_providers {
21
+ hcloud = {
22
+ source = "hetznercloud/hcloud"
23
+ version = "~> 1.49"
24
+ }
25
+ }
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltrixsecops/app-sdk",
3
- "version": "3.2.0",
3
+ "version": "3.5.1",
4
4
  "description": "Official SDK for building Veltrix Security-as-Code apps — typed pipeline handler contracts, lifecycle hook types, manifest types, and React hooks.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -58,10 +58,42 @@
58
58
  "default": "./dist/ui/index.cjs"
59
59
  }
60
60
  },
61
+ "./byol": {
62
+ "import": {
63
+ "types": "./dist/byol/index.d.ts",
64
+ "default": "./dist/byol/index.js"
65
+ },
66
+ "require": {
67
+ "types": "./dist/byol/index.d.cts",
68
+ "default": "./dist/byol/index.cjs"
69
+ }
70
+ },
71
+ "./connections": {
72
+ "import": {
73
+ "types": "./dist/connections/index.d.ts",
74
+ "default": "./dist/connections/index.js"
75
+ },
76
+ "require": {
77
+ "types": "./dist/connections/index.d.cts",
78
+ "default": "./dist/connections/index.cjs"
79
+ }
80
+ },
81
+ "./opentofu": {
82
+ "import": {
83
+ "types": "./dist/opentofu/index.d.ts",
84
+ "default": "./dist/opentofu/index.js"
85
+ },
86
+ "require": {
87
+ "types": "./dist/opentofu/index.d.cts",
88
+ "default": "./dist/opentofu/index.cjs"
89
+ }
90
+ },
91
+ "./opentofu/modules/*": "./opentofu/modules/*",
61
92
  "./package.json": "./package.json"
62
93
  },
63
94
  "files": [
64
95
  "dist",
96
+ "opentofu",
65
97
  "README.md"
66
98
  ],
67
99
  "scripts": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client/inventory.ts","../src/client/connectivity.ts","../src/client/environments.ts","../src/client/credentials.ts","../src/client/index.ts"],"sourcesContent":["// ========================================================================\n// Inventory — the deployment targets an app can deploy configuration to.\n//\n// \"Inventory\" is the app-facing name for the platform's *components*: the\n// servers (hostname/port), domains, and IP/CIDR ranges a customer has\n// registered as deploy targets. These helpers are a typed, convenient\n// surface over the platform's components API (/api/components), enriched\n// with `domains` and `ipRanges`.\n//\n// Framework-free (no React) — safe to import from any client code. Every\n// call goes through the same `authFetch` the '/client' subpath exports, so\n// requests carry the platform's Authorization header. Non-2xx responses are\n// surfaced as thrown Errors carrying the platform's error text.\n// ========================================================================\n\nimport type { InventoryItem, InventoryItemInput } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's components (inventory) API. */\nconst INVENTORY_API = '/api/components'\n\n/**\n * Loosely-typed shape of a raw component as returned by the platform, before\n * it is normalized down to the {@link InventoryItem} surface.\n */\ninterface RawInventoryItem {\n id: string\n hostname?: string\n port?: string\n type?: string[]\n domains?: string[]\n ipRanges?: string[]\n tags?: Array<{ id: string; name: string }>\n connectivityProviderId?: string | null\n credentialId?: string | null\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function inventoryError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** Normalize a raw platform component into the typed InventoryItem surface. */\nfunction toInventoryItem(raw: RawInventoryItem): InventoryItem {\n return {\n id: String(raw.id),\n hostname: raw.hostname ?? '',\n port: raw.port ?? undefined,\n type: Array.isArray(raw.type) ? raw.type : undefined,\n domains: Array.isArray(raw.domains) ? raw.domains : [],\n ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) }))\n : [],\n connectivityProviderId: raw.connectivityProviderId ?? null,\n credentialId: raw.credentialId ?? null,\n }\n}\n\n/**\n * A platform Tool. Each installed app is upserted as a Tool keyed by its\n * manifest `name`, and inventory items (components) belong to a tool.\n */\nexport interface Tool {\n id: string\n name: string\n vendor?: string\n}\n\n/**\n * Resolve the platform Tool for an app by its manifest name (the platform\n * upserts `Tool.name === app name`). The tool id is required by the platform\n * when creating an inventory item, so call this once and pass the id as\n * `toolId` to {@link addInventoryItem}. Returns null when no tool matches.\n *\n * GET /api/tools (the endpoint is paginated — `{ data, pagination }` — or a\n * bare array; both are handled).\n */\nexport async function resolveTool(name: string): Promise<Tool | null> {\n const res = await authFetch('/api/tools')\n if (!res.ok) throw await inventoryError(res)\n const body = (await res.json()) as unknown\n const tools: Tool[] = Array.isArray(body)\n ? (body as Tool[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: Tool[] }).data)\n : []\n return tools.find((tool) => tool.name === name) ?? null\n}\n\n/** List the customer's inventory (deployment targets). GET /api/components */\nexport async function listInventory(): Promise<InventoryItem[]> {\n const res = await authFetch(INVENTORY_API)\n if (!res.ok) throw await inventoryError(res)\n const data = (await res.json()) as RawInventoryItem[]\n return Array.isArray(data) ? data.map(toInventoryItem) : []\n}\n\n/** Add a new inventory item (deployment target). POST /api/components */\nexport async function addInventoryItem(input: InventoryItemInput): Promise<InventoryItem> {\n const res = await authFetch(INVENTORY_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Update an existing inventory item. PUT /api/components/:id */\nexport async function updateInventoryItem(\n id: string,\n input: InventoryItemInput,\n): Promise<InventoryItem> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Remove an inventory item. DELETE /api/components/:id */\nexport async function removeInventoryItem(id: string): Promise<void> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await inventoryError(res)\n}\n","// ========================================================================\n// Connectivity providers — the customer's ZTNA providers (Tailscale,\n// WireGuard, ZeroTier, …), platform-managed under Settings → Connectivity.\n//\n// A thin reader over the platform's connectivity-providers API\n// (/api/connectivity-providers), used to populate the ZTNA link picker when\n// creating or editing an Access Server (a component). Framework-free — every\n// call goes through the same `authFetch` the '/client' subpath exports.\n// ========================================================================\n\nimport type { ConnectivityProviderRef } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's connectivity-providers API. */\nconst CONNECTIVITY_PROVIDERS_API = '/api/connectivity-providers'\n\ninterface RawConnectivityProvider {\n id: string\n name?: string\n providerType?: string\n status?: string\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function providerError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's ZTNA connectivity providers, used to populate the Access\n * Server link picker. GET /api/connectivity-providers (the endpoint may return\n * a bare array or a paginated `{ data, ... }` shape — both are handled).\n */\nexport async function listConnectivityProviders(): Promise<ConnectivityProviderRef[]> {\n const res = await authFetch(CONNECTIVITY_PROVIDERS_API)\n if (!res.ok) throw await providerError(res)\n const body = (await res.json()) as unknown\n const providers: RawConnectivityProvider[] = Array.isArray(body)\n ? (body as RawConnectivityProvider[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawConnectivityProvider[] }).data)\n : []\n return providers.map((provider) => ({\n id: String(provider.id),\n name: provider.name ?? '',\n providerType: provider.providerType ?? undefined,\n status: provider.status ?? undefined,\n }))\n}\n","// ========================================================================\n// Environments — the deployment scopes that determine where a configuration is\n// pushed. Environments are the customer's platform tags (dev/staging/prod, …);\n// a Connection (credential) or Access Server (component) is tied to one by\n// carrying the environment id in its `tagIds`.\n//\n// A thin reader over the platform's environments API (/api/environments), used\n// to populate the Environment picker on the Connections and Access Servers\n// pages. Framework-free — every call goes through the same `authFetch` the\n// '/client' subpath exports.\n// ========================================================================\n\nimport type { EnvironmentRef } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's environments API. */\nconst ENVIRONMENTS_API = '/api/environments'\n\ninterface RawEnvironment {\n id: string\n name?: string\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function environmentError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's environments (deployment scopes). GET /api/environments\n * (the endpoint may return a bare array or a paginated `{ data, ... }` shape —\n * both are handled). Use the returned id as a `tagIds` entry to tie a Connection\n * or Access Server to that environment.\n */\nexport async function listEnvironments(): Promise<EnvironmentRef[]> {\n const res = await authFetch(ENVIRONMENTS_API)\n if (!res.ok) throw await environmentError(res)\n const body = (await res.json()) as unknown\n const rows: RawEnvironment[] = Array.isArray(body)\n ? (body as RawEnvironment[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawEnvironment[] }).data)\n : []\n return rows.map((env) => ({ id: String(env.id), name: env.name ?? '' }))\n}\n","// ========================================================================\n// Credentials — how an app authenticates to a server (\"connection\").\n//\n// A \"connection\" pairs a server (a platform *component* — see inventory.ts)\n// with a *credential*: the account and write-only secret used to reach that\n// server. These helpers are a typed surface over the platform's credentials\n// API (POST /api/credentials, GET /api/tools/:toolId/credentials, PUT/DELETE\n// /api/credentials/:id).\n//\n// Framework-free (no React). Every call goes through the same `authFetch` the\n// '/client' subpath exports, so requests carry the platform's Authorization\n// header. Non-2xx responses are surfaced as thrown Errors carrying the\n// platform's error text.\n//\n// SECURITY: `listCredentials` returns a REDACTED {@link CredentialSummary} —\n// secret material (password / apiToken / certificate) is dropped before it\n// reaches app code, so secrets are never held in memory or logged. Only whether\n// a secret exists is surfaced (`hasSecret`). Secrets are write-only: they can be\n// set via create/update, never read back.\n// ========================================================================\n\nimport type { CredentialInput, CredentialSummary } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's credentials API. */\nconst CREDENTIALS_API = '/api/credentials'\n\n/**\n * Loosely-typed shape of a raw credential as returned by the platform, before\n * it is redacted down to the {@link CredentialSummary} surface. The secret\n * fields (`password` / `apiToken` / `certificate`) are read here only to derive\n * `hasSecret` — they are never carried into app-visible data.\n */\ninterface RawCredential {\n id: string\n name?: string\n username?: string\n type?: string | null\n endpoint?: string | null\n toolId?: string\n // The platform redacts secrets from credential responses and surfaces only\n // whether each is set via these flags. Older platforms may still send the\n // secret fields instead — both shapes are handled below.\n hasPassword?: boolean\n hasApiToken?: boolean\n hasCertificate?: boolean\n password?: string | null\n apiToken?: string | null\n certificate?: string | null\n tags?: Array<{ id: string; name: string }>\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function credentialError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * Redact a raw platform credential down to the app-visible summary, dropping\n * every secret field and surfacing only whether a secret is stored.\n */\nfunction toCredentialSummary(raw: RawCredential): CredentialSummary {\n // Prefer the redacted has* flags; fall back to the presence of the secret\n // fields themselves for older platforms that still return them.\n const hasSecret = Boolean(\n raw.hasApiToken ||\n raw.hasPassword ||\n (raw.apiToken && raw.apiToken.length > 0) ||\n (raw.password && raw.password.length > 0),\n )\n return {\n id: String(raw.id),\n name: raw.name ?? '',\n username: raw.username ?? '',\n type: raw.type ?? null,\n endpoint: raw.endpoint ?? null,\n toolId: raw.toolId ?? '',\n hasSecret,\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) }))\n : [],\n }\n}\n\n/**\n * List the redacted credentials registered for a tool. GET\n * /api/tools/:toolId/credentials. Secrets are stripped before return — see the\n * module's SECURITY note. Returns an empty array when the tool has none.\n */\nexport async function listCredentials(toolId: string): Promise<CredentialSummary[]> {\n const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`)\n if (!res.ok) throw await credentialError(res)\n const data = (await res.json()) as unknown\n const rows: RawCredential[] = Array.isArray(data)\n ? (data as RawCredential[])\n : Array.isArray((data as { data?: unknown })?.data)\n ? ((data as { data: RawCredential[] }).data)\n : []\n return rows.map(toCredentialSummary)\n}\n\n/**\n * Create a credential. POST /api/credentials. The platform requires `name`,\n * `username`, `password`, `toolId`, and `tagIds` — this helper defaults\n * `tagIds` to `[]` and `password` to `''` (valid for token-only auth, where the\n * secret travels in `apiToken`). Returns the new credential's id.\n */\nexport async function createCredential(input: CredentialInput): Promise<{ id: string }> {\n const res = await authFetch(CREDENTIALS_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: input.name,\n username: input.username,\n password: input.password ?? '',\n apiToken: input.apiToken,\n type: input.type,\n endpoint: input.endpoint,\n toolId: input.toolId,\n tagIds: input.tagIds ?? [],\n }),\n })\n if (!res.ok) throw await credentialError(res)\n const body = (await res.json()) as { id?: string }\n return { id: String(body.id) }\n}\n\n/**\n * Update a credential. PUT /api/credentials/:id. Only the fields you pass are\n * changed; omit `password`/`apiToken` to leave the stored secret untouched.\n */\nexport async function updateCredential(\n id: string,\n input: Partial<CredentialInput>,\n): Promise<{ id: string }> {\n const body: Record<string, unknown> = {}\n if (input.name !== undefined) body.name = input.name\n if (input.username !== undefined) body.username = input.username\n if (input.password !== undefined) body.password = input.password\n if (input.apiToken !== undefined) body.apiToken = input.apiToken\n if (input.type !== undefined) body.type = input.type\n if (input.endpoint !== undefined) body.endpoint = input.endpoint\n if (input.tagIds !== undefined) body.tagIds = input.tagIds\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!res.ok) throw await credentialError(res)\n const result = (await res.json().catch(() => ({}))) as { id?: string }\n return { id: result.id ? String(result.id) : id }\n}\n\n/** Remove a credential. DELETE /api/credentials/:id. */\nexport async function removeCredential(id: string): Promise<void> {\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await credentialError(res)\n}\n","// ========================================================================\n// Client runtime contract — how app client bundles talk to the host.\n//\n// App client code is packaged as a hermetic ESM bundle in which `react`,\n// `react-dom`, `react/jsx-runtime`, and every `@veltrixsecops/app-sdk`\n// subpath are compile-time shims that read the host-provided runtime from\n// `globalThis.__VELTRIX_APP_RUNTIME__`. The platform installs that global\n// (with ITS React instance, the shared AppContext, and an authenticated\n// fetch) before dynamically importing any app bundle, so app components\n// render inside the host React tree with working hooks and context.\n//\n// App authors: import from '@veltrixsecops/app-sdk/client' (and /hooks) —\n// never bundle your own copy of react. Use `authFetch` for calls to your\n// app's server routes (/api/apps/<app-id>/...): plain fetch() lacks the\n// platform's Authorization header and will receive 401s.\n// ========================================================================\n\nimport type { ComponentType, Context, LazyExoticComponent } from 'react'\nimport type { AppContextValue } from '../hooks/use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\nexport type { AppPermissionsApi, PermissionEntry, PermissionCheckOptions } from '../types/platform'\n\n// Inventory — typed helpers over the platform's components API (deployment\n// targets: servers, domains, IP/CIDR ranges). Framework-free; they use the\n// `authFetch` exported below internally.\nexport {\n listInventory,\n addInventoryItem,\n updateInventoryItem,\n removeInventoryItem,\n resolveTool,\n} from './inventory'\nexport type { Tool } from './inventory'\nexport type { InventoryItem, InventoryItemInput } from '../types/platform'\n\n// Connectivity providers — reader over the customer's ZTNA providers, used to\n// populate the Access Server link picker. Framework-free; uses `authFetch`.\nexport { listConnectivityProviders } from './connectivity'\nexport type { ConnectivityProviderRef } from '../types/platform'\n\n// Environments — reader over the customer's deployment scopes, used to populate\n// the Environment picker on Connections + Access Servers. Framework-free.\nexport { listEnvironments } from './environments'\nexport type { EnvironmentRef } from '../types/pipeline'\n\n// Credentials — typed helpers over the platform's credentials API. Paired with\n// a server (component) these form a \"connection\". Secrets are write-only:\n// `listCredentials` returns redacted summaries only. Framework-free; they use\n// the `authFetch` exported below internally.\nexport {\n listCredentials,\n createCredential,\n updateCredential,\n removeCredential,\n} from './credentials'\nexport type { Credential, CredentialSummary, CredentialInput } from '../types/platform'\n\n/** Name of the global the platform installs before loading app bundles. */\nexport const HOST_RUNTIME_GLOBAL = '__VELTRIX_APP_RUNTIME__'\n\n/**\n * The runtime surface the platform exposes to app client bundles.\n * The react/reactDom/jsxRuntime members are the host's own module objects —\n * app bundles are compiled with shims that re-export them, guaranteeing a\n * single React instance per page.\n */\nexport interface VeltrixHostRuntime {\n /** The host's `react` module object. */\n react: unknown\n /** The host's `react-dom` module object. */\n reactDom: unknown\n /** The host's `react-dom/client` module object. */\n reactDomClient?: unknown\n /** The host's `react/jsx-runtime` module object. */\n jsxRuntime: unknown\n /** Shared app context — the host wraps app pages in its Provider. */\n AppContext: Context<AppContextValue | null>\n /** fetch() with the platform's Authorization header attached. */\n authFetch: (input: string, init?: RequestInit) => Promise<Response>\n /**\n * The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,\n * `.../hooks`, and `.../client` imports (useAppContext, AppContext,\n * usePipelineStatus, authFetch, getHostRuntime, ...).\n */\n sdk: Record<string, unknown>\n /**\n * The platform's design-system components and hooks, host-owned so they\n * share the single host React instance. Keyed by the exact component/hook\n * names re-exported from `@veltrixsecops/app-sdk/ui` (Button, Input, Card,\n * DataTable, useToast, ...). Present only inside the platform.\n */\n ui?: Record<string, unknown>\n /**\n * Platform-scoped permission checks (RBAC/IdP hardening, Wave C4) —\n * `opts.appId` is NOT defaulted here (omit for a platform-scoped check).\n * App page code should prefer `usePermissions()`\n * (`@veltrixsecops/app-sdk/hooks`), which defaults `opts.appId` to the\n * app's own id. Optional for compatibility with a host that hasn't\n * deployed this surface yet.\n */\n permissions?: AppPermissionsApi\n}\n\n/** Read the host runtime, or null outside the platform (tests, storybook). */\nexport function getHostRuntime(): VeltrixHostRuntime | null {\n const runtime = (globalThis as Record<string, unknown>)[HOST_RUNTIME_GLOBAL]\n return (runtime as VeltrixHostRuntime) ?? null\n}\n\n/** Read the host runtime, throwing a diagnosable error when absent. */\nexport function requireHostRuntime(): VeltrixHostRuntime {\n const runtime = getHostRuntime()\n if (!runtime) {\n throw new Error(\n 'Veltrix host runtime not found — app client bundles only run inside the ' +\n `Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`,\n )\n }\n return runtime\n}\n\n/**\n * fetch() that carries the platform's Authorization header. Required for an\n * app page to call its own server routes (/api/apps/<app-id>/...), which are\n * bearer-token protected. Falls back to plain fetch outside the platform.\n */\nexport function authFetch(input: string, init?: RequestInit): Promise<Response> {\n const runtime = getHostRuntime()\n if (runtime) return runtime.authFetch(input, init)\n return fetch(input, init)\n}\n\n/** A sidebar entry contributed by the app's client entry module. */\nexport interface AppSidebarItem {\n path: string\n label: string\n icon?: string\n}\n\n/**\n * Shape of the default export of an app's `client/index.tsx`.\n * `pages` keys must match `manifest.client.pages[].component`.\n */\nexport interface AppClientModule {\n id: string\n pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>\n sidebarItems?: AppSidebarItem[]\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pipeline/define-validator.ts","../src/pipeline/define-deployer.ts","../src/pipeline/define-rollback-handler.ts","../src/pipeline/define-health-checker.ts","../src/pipeline/define-drift-detector.ts"],"sourcesContent":["import type { PipelineContext, ValidationResult } from '../types/pipeline'\n\n/**\n * Define a validation handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineValidator } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineValidator(async (ctx) => {\n * const errors = []\n * const name = ctx.canvas.sections[0]?.fields['name']\n * if (!name) {\n * errors.push({ field: 'name', message: 'Name is required', code: 'REQUIRED' })\n * }\n * return { valid: errors.length === 0, errors, warnings: [] }\n * })\n * ```\n */\nexport function defineValidator(\n handler: (ctx: PipelineContext) => Promise<ValidationResult>,\n): (ctx: PipelineContext) => Promise<ValidationResult> {\n return handler\n}\n","import type { DeployContext, DeployResult } from '../types/pipeline'\n\n/**\n * Define a deploy handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineDeployer(async (ctx) => {\n * const { component, credential, connectivity, canvas } = ctx\n * // Push configuration to the tool\n * return { success: true, message: 'Deployed', rollbackData: previousState }\n * })\n * ```\n */\nexport function defineDeployer(\n handler: (ctx: DeployContext) => Promise<DeployResult>,\n): (ctx: DeployContext) => Promise<DeployResult> {\n return handler\n}\n","import type { RollbackContext, RollbackResult } from '../types/pipeline'\n\n/**\n * Define a rollback handler for a configuration type.\n */\nexport function defineRollbackHandler(\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\n): (ctx: RollbackContext) => Promise<RollbackResult> {\n return handler\n}\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\n\n/**\n * Define a health check handler for a configuration type.\n */\nexport function defineHealthChecker(\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\n return handler\n}\n","import type { DriftContext, DriftResult } from '../types/pipeline'\n\n/**\n * Define a drift detection handler for a configuration type.\n */\nexport function defineDriftDetector(\n handler: (ctx: DriftContext) => Promise<DriftResult>,\n): (ctx: DriftContext) => Promise<DriftResult> {\n return handler\n}\n"],"mappings":";AAmBO,SAAS,gBACd,SACqD;AACrD,SAAO;AACT;;;ACPO,SAAS,eACd,SAC+C;AAC/C,SAAO;AACT;;;ACfO,SAAS,sBACd,SACmD;AACnD,SAAO;AACT;;;ACJO,SAAS,oBACd,SACyD;AACzD,SAAO;AACT;;;ACJO,SAAS,oBACd,SAC6C;AAC7C,SAAO;AACT;","names":[]}