@microagi/alchemy-gcp 0.2.1 → 0.5.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 (107) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +54 -2
  3. package/lib/ArtifactRegistry/Operations.d.ts +47 -0
  4. package/lib/ArtifactRegistry/Operations.d.ts.map +1 -0
  5. package/lib/ArtifactRegistry/Operations.js +47 -0
  6. package/lib/ArtifactRegistry/Operations.js.map +1 -0
  7. package/lib/ArtifactRegistry/Repository.d.ts +263 -0
  8. package/lib/ArtifactRegistry/Repository.d.ts.map +1 -0
  9. package/lib/ArtifactRegistry/Repository.js +257 -0
  10. package/lib/ArtifactRegistry/Repository.js.map +1 -0
  11. package/lib/ArtifactRegistry/Types.d.ts +32 -0
  12. package/lib/ArtifactRegistry/Types.d.ts.map +1 -0
  13. package/lib/ArtifactRegistry/Types.js +12 -0
  14. package/lib/ArtifactRegistry/Types.js.map +1 -0
  15. package/lib/ArtifactRegistry/Validation.d.ts +43 -0
  16. package/lib/ArtifactRegistry/Validation.d.ts.map +1 -0
  17. package/lib/ArtifactRegistry/Validation.js +95 -0
  18. package/lib/ArtifactRegistry/Validation.js.map +1 -0
  19. package/lib/ArtifactRegistry/index.d.ts +4 -0
  20. package/lib/ArtifactRegistry/index.d.ts.map +1 -0
  21. package/lib/ArtifactRegistry/index.js +2 -0
  22. package/lib/ArtifactRegistry/index.js.map +1 -0
  23. package/lib/Providers.d.ts.map +1 -1
  24. package/lib/Providers.js +13 -1
  25. package/lib/Providers.js.map +1 -1
  26. package/lib/Run/IamMember.d.ts +70 -0
  27. package/lib/Run/IamMember.d.ts.map +1 -0
  28. package/lib/Run/IamMember.js +65 -0
  29. package/lib/Run/IamMember.js.map +1 -0
  30. package/lib/Run/IamSync.d.ts +80 -0
  31. package/lib/Run/IamSync.d.ts.map +1 -0
  32. package/lib/Run/IamSync.js +73 -0
  33. package/lib/Run/IamSync.js.map +1 -0
  34. package/lib/Run/Job.d.ts +167 -0
  35. package/lib/Run/Job.d.ts.map +1 -0
  36. package/lib/Run/Job.js +199 -0
  37. package/lib/Run/Job.js.map +1 -0
  38. package/lib/Run/Operations.d.ts +47 -0
  39. package/lib/Run/Operations.d.ts.map +1 -0
  40. package/lib/Run/Operations.js +46 -0
  41. package/lib/Run/Operations.js.map +1 -0
  42. package/lib/Run/Service.d.ts +193 -0
  43. package/lib/Run/Service.d.ts.map +1 -0
  44. package/lib/Run/Service.js +247 -0
  45. package/lib/Run/Service.js.map +1 -0
  46. package/lib/Run/Validation.d.ts +54 -0
  47. package/lib/Run/Validation.d.ts.map +1 -0
  48. package/lib/Run/Validation.js +129 -0
  49. package/lib/Run/Validation.js.map +1 -0
  50. package/lib/Run/index.d.ts +51 -0
  51. package/lib/Run/index.d.ts.map +1 -0
  52. package/lib/Run/index.js +4 -0
  53. package/lib/Run/index.js.map +1 -0
  54. package/lib/Sqladmin/Database.d.ts +82 -0
  55. package/lib/Sqladmin/Database.d.ts.map +1 -0
  56. package/lib/Sqladmin/Database.js +87 -0
  57. package/lib/Sqladmin/Database.js.map +1 -0
  58. package/lib/Sqladmin/Instance.d.ts +234 -0
  59. package/lib/Sqladmin/Instance.d.ts.map +1 -0
  60. package/lib/Sqladmin/Instance.js +251 -0
  61. package/lib/Sqladmin/Instance.js.map +1 -0
  62. package/lib/Sqladmin/Operations.d.ts +41 -0
  63. package/lib/Sqladmin/Operations.d.ts.map +1 -0
  64. package/lib/Sqladmin/Operations.js +42 -0
  65. package/lib/Sqladmin/Operations.js.map +1 -0
  66. package/lib/Sqladmin/Types.d.ts +82 -0
  67. package/lib/Sqladmin/Types.d.ts.map +1 -0
  68. package/lib/Sqladmin/Types.js +28 -0
  69. package/lib/Sqladmin/Types.js.map +1 -0
  70. package/lib/Sqladmin/User.d.ts +110 -0
  71. package/lib/Sqladmin/User.d.ts.map +1 -0
  72. package/lib/Sqladmin/User.js +156 -0
  73. package/lib/Sqladmin/User.js.map +1 -0
  74. package/lib/Sqladmin/Validation.d.ts +60 -0
  75. package/lib/Sqladmin/Validation.d.ts.map +1 -0
  76. package/lib/Sqladmin/Validation.js +125 -0
  77. package/lib/Sqladmin/Validation.js.map +1 -0
  78. package/lib/Sqladmin/index.d.ts +9 -0
  79. package/lib/Sqladmin/index.d.ts.map +1 -0
  80. package/lib/Sqladmin/index.js +5 -0
  81. package/lib/Sqladmin/index.js.map +1 -0
  82. package/lib/index.d.ts +3 -0
  83. package/lib/index.d.ts.map +1 -1
  84. package/lib/index.js +3 -0
  85. package/lib/index.js.map +1 -1
  86. package/package.json +5 -4
  87. package/src/ArtifactRegistry/Operations.ts +79 -0
  88. package/src/ArtifactRegistry/Repository.ts +624 -0
  89. package/src/ArtifactRegistry/Types.ts +49 -0
  90. package/src/ArtifactRegistry/Validation.ts +112 -0
  91. package/src/ArtifactRegistry/index.ts +10 -0
  92. package/src/Providers.ts +21 -0
  93. package/src/Run/IamMember.ts +79 -0
  94. package/src/Run/IamSync.ts +134 -0
  95. package/src/Run/Job.ts +453 -0
  96. package/src/Run/Operations.ts +77 -0
  97. package/src/Run/Service.ts +541 -0
  98. package/src/Run/Validation.ts +153 -0
  99. package/src/Run/index.ts +52 -0
  100. package/src/Sqladmin/Database.ts +209 -0
  101. package/src/Sqladmin/Instance.ts +620 -0
  102. package/src/Sqladmin/Operations.ts +73 -0
  103. package/src/Sqladmin/Types.ts +101 -0
  104. package/src/Sqladmin/User.ts +333 -0
  105. package/src/Sqladmin/Validation.ts +148 -0
  106. package/src/Sqladmin/index.ts +19 -0
  107. package/src/index.ts +3 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,165 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@microagi/alchemy-gcp`. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this package
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 0.5.0 — 2026-06-07
8
+
9
+ Artifact Registry (artifactregistry v1) — Docker repositories, both
10
+ standard (self-hosted builds) and remote (pull-through caches for
11
+ Docker Hub and arbitrary upstreams like `nvcr.io`).
12
+
13
+ ### Added
14
+
15
+ - **`GCP.ArtifactRegistryRepository`** — Artifact Registry repository
16
+ resource for `format: "DOCKER"`. Supports:
17
+ - `mode: "STANDARD_REPOSITORY"` — repository hosts images uploaded
18
+ by the project (CI builds, etc.).
19
+ - `mode: "REMOTE_REPOSITORY"` — pull-through cache. The upstream is
20
+ either a Google-known preset (`{ publicRepository: "DOCKER_HUB" }`)
21
+ or an arbitrary HTTPS Docker registry
22
+ (`{ customRepository: { uri: "https://nvcr.io" } }`). Optional
23
+ `upstreamCredentials` for authenticated upstreams (Secret
24
+ Manager-backed password).
25
+ - Mutable-via-patch fields: `description`, `labels`, `dockerConfig`
26
+ (`immutableTags`), `cleanupPolicies`, `cleanupPolicyDryRun` —
27
+ diff'd against observed cloud state and patched with a precise
28
+ `updateMask`.
29
+ - Replace triggers: `project`, `location`, `name`, `format`, `mode`,
30
+ `kmsKeyName`, and any change to `remoteRepositoryConfig` (GCP
31
+ refuses in-place updates here).
32
+ - Standard adoption gate via `alchemy_*` labels — pre-existing
33
+ repositories without our labels surface as `Unowned` and require
34
+ `--adopt` to take over.
35
+ - Same API-enable propagation retry (15s × 20 ≈ 5 min) as
36
+ `Cluster` / `Service` / `SqlInstance` for the race between
37
+ `ApiEnable` and the first create.
38
+ - **Plan-time validation** — repo-name regex
39
+ (`/^[a-z0-9][a-z0-9-]{0,62}$/`), and the
40
+ `mode` ↔ `remoteRepositoryConfig` invariant (REMOTE requires a
41
+ docker upstream; STANDARD forbids a remote config). Fails fast with
42
+ typed `ConfigError` before any state mutation.
43
+ - **Branded literal types** — `ArtifactRegistryFormat` (DOCKER,
44
+ MAVEN, …) and `ArtifactRegistryMode`
45
+ (STANDARD/REMOTE/VIRTUAL_REPOSITORY) with `(string & {})` tails for
46
+ forward-compat with new server-side values.
47
+
48
+ ### LRO
49
+
50
+ - `ArtifactRegistry/Operations.ts` — Cloud Run-style poller. AR
51
+ operations use the standard `GoogleLongrunningOperation` shape
52
+ (`.done: boolean`), distinct from Sqladmin's tri-state `.status`.
53
+ Operation names are fully-qualified
54
+ (`projects/{p}/locations/{l}/operations/{id}`).
55
+
56
+ ## 0.4.0 — 2026-06-07
57
+
58
+ Cloud SQL (sqladmin v1) — Postgres instance + database + user, with
59
+ first-class IAM database authentication.
60
+
61
+ ### Added
62
+
63
+ - **`GCP.SqlInstance`** — Cloud SQL for Postgres instance. Tier,
64
+ edition, disk auto-resize, IP configuration (public IPv4 + private
65
+ IP via PSA), backups + PITR, database flags, deletion protection,
66
+ zonal/regional HA. Optimistic concurrency via the live
67
+ `settings.settingsVersion` round-trip (Cloud SQL's `etag` field is
68
+ deprecated). API-enable-propagation 403 retry mirroring the
69
+ `Cluster` / `Service` providers.
70
+ - **`GCP.SqlDatabase`** — logical Postgres database. Thin resource —
71
+ name, charset, collation. No labels on the GCP side so adoption is
72
+ best-effort (no `Unowned` wrapper).
73
+ - **`GCP.SqlUser`** — Postgres role with three auth modes:
74
+ - `BUILT_IN` — username + `Redacted` password.
75
+ - `CLOUD_IAM_USER` — human IAM principal authenticated via the
76
+ Cloud SQL Auth Proxy / IAM token exchange.
77
+ - `CLOUD_IAM_SERVICE_ACCOUNT` — GCP service-account principal
78
+ (name = SA email minus the `.gserviceaccount.com` tail).
79
+ Plan-time validation rejects passwords on IAM users and missing
80
+ passwords on `BUILT_IN`. Forward-compat `(string & {})` tail keeps
81
+ newer IAM variants (`CLOUD_IAM_GROUP*`, `ENTRAID_USER`) reachable.
82
+ - **Branded literal types** from `Sqladmin/Types.ts` —
83
+ `PostgresVersion` (15/16/17 only — narrower than distilled's
84
+ open-tailed enum so typos are caught at compile time), `SqlEdition`,
85
+ `SqlDataDiskType`, `SqlAvailabilityType`, `SqlActivationPolicy`,
86
+ `SqlUserType`.
87
+ - **Plan-time validation** — instance name regex (1–98 chars,
88
+ RFC 1123 label subset), database name length, user password/type
89
+ consistency. Fails fast with a typed `ConfigError` before any state
90
+ mutation.
91
+ - **`BadRequest` passthroughs** on `Instance`/`Database`/`User`
92
+ create/patch/delete — billing-not-enabled, missing PSA peering,
93
+ and invalid tier get remediation hints; anything else propagates
94
+ verbatim.
95
+
96
+ ### Notes
97
+
98
+ - Sqladmin operations use `status: "DONE"` (distinct from the
99
+ `done: boolean` shape Cloud Resource Manager / Cloud Run / Lustre
100
+ use). `Sqladmin/Operations.ts` polls `getOperations({ project,
101
+ operation })` accordingly.
102
+ - `SqlInstance` adoption is label-gated through
103
+ `settings.userLabels`. `SqlDatabase` and `SqlUser` cannot carry
104
+ labels, so they trust the logical id / parent address alone — be
105
+ cautious with `--adopt` on those.
106
+
107
+ ## 0.3.0 — 2026-05-11
108
+
109
+ Cloud Run v2 — production polish on the Service + Job + IAM surface.
110
+
111
+ ### Added
112
+
113
+ - **`GCP.Service`** — Cloud Run v2 HTTP service. Traffic routing,
114
+ ingress controls (`INGRESS_TRAFFIC_ALL` / `INTERNAL_ONLY` /
115
+ `INTERNAL_LOAD_BALANCER`), launch-stage gating, server-side
116
+ template diffing, label-gated adoption, and full LRO polling.
117
+ - **`GCP.Job`** — Cloud Run v2 batch workload. Declarative
118
+ definition of the Job (template, parallelism, task count, retries,
119
+ GPU node selectors); execution is imperative via
120
+ `runProjectsLocationsJobs` (documented in the resource JSDoc).
121
+ - **`GCP.serviceIamMember`** / **`GCP.jobIamMember`** — target-side
122
+ IAM binding helpers. Provider unions all bindings per target and
123
+ writes a single `setIamPolicy` per reconcile, preserving foreign
124
+ roles and members. Etag round-trip absorbs CI/human contention.
125
+ - **Clean type re-exports** from the top level — `GCP.RevisionTemplate`,
126
+ `GCP.ExecutionTemplate`, `GCP.TaskTemplate`, `GCP.Container`,
127
+ `GCP.TrafficTarget`, `GCP.VpcAccess`, `GCP.NodeSelector`, etc. —
128
+ so consumers no longer need to import from `@distilled.cloud/gcp`
129
+ directly.
130
+ - **Plan-time validation** for Cloud Run resources — name regex
131
+ (`<50 chars`, RFC 1123 label subset), reserved label/annotation
132
+ namespaces (`run.googleapis.com/`, `cloud.googleapis.com/`,
133
+ `serving.knative.dev/`, `autoscaling.knative.dev/`), and
134
+ template-must-have-containers. Fails fast with a typed
135
+ `ConfigError` before any state mutation.
136
+ - **`BadRequest` passthroughs** on Cloud Run create + patch — wrap
137
+ the underlying GCP message with a remediation hint for billing
138
+ not enabled, image-not-in-approved-registry, and reserved-namespace
139
+ failures.
140
+
141
+ ### Changed
142
+
143
+ - **Optimistic concurrency** on Cloud Run Service + Job — patch and
144
+ delete now pass the observed `etag` so concurrent edits (e.g. via
145
+ `gcloud run services update`) surface as `Conflict` instead of
146
+ silently overwriting.
147
+ - README rewritten with categorised resource sections and Cloud Run
148
+ examples.
149
+
150
+ ### Notes
151
+
152
+ `apps/cluster` and other downstream stacks bumping past `0.2.x` should
153
+ swap `invokerIamDisabled: true` (sledgehammer) for the declarative
154
+ `serviceIamMember(svc, "Public", { role: "roles/run.invoker", member: "allUsers" })`
155
+ binding where possible — IAM history is auditable; the flag is not.
156
+
157
+ ## 0.2.x
158
+
159
+ Pre-0.3 history is in `git log` — see commits up to and including
160
+ `c6bcb9d Add Cloud Run v2 Service resource`. Highlights:
161
+
162
+ - 0.2.0 — initial Cloud Run Service support (no IAM, no Job).
163
+ - 0.1.x — Project, ApiEnable, Cluster, NodePool, Network, Subnetwork,
164
+ PsaConnection, GlobalAddress, SharedVpcHost, SharedVpcServiceProject,
165
+ ManagedLustreInstance.
package/README.md CHANGED
@@ -79,27 +79,79 @@ bun alchemy destroy ./alchemy.run.ts
79
79
 
80
80
  `Alchemy.localState()` writes Alchemy's resource state to `.alchemy/` next to the stack file. `Alchemy.inMemoryState()` is fine for tests but loses state between runs. For team use, swap in `httpStateStore()`.
81
81
 
82
- Long-running operations (`createProjects`, `patchProjects`, GKE cluster ops) are polled internally; reconcilers follow the [Alchemy reconciler doctrine](https://v2.alchemy.run/concepts/resource-lifecycle) (single observe → ensure → sync → return flow that converges from any starting state, including adoption).
82
+ Long-running operations (`createProjects`, `patchProjects`, GKE cluster ops, Cloud Run service deploys) are polled internally; reconcilers follow the [Alchemy reconciler doctrine](https://v2.alchemy.run/concepts/resource-lifecycle) (single observe → ensure → sync → return flow that converges from any starting state, including adoption).
83
83
 
84
84
  ## Resources
85
85
 
86
+ ### Foundation
86
87
  - **`GCP.Project`** — projects under an org or folder, with optional billing-account attach.
87
88
  - **`GCP.ApiEnable`** — project-level GCP service enablement.
89
+
90
+ ### Compute
88
91
  - **`GCP.Cluster`** — Standard GKE cluster.
89
92
  - **`GCP.NodePool`** — node pool attached to a cluster, including accelerator (GPU) configurations.
93
+
94
+ ### Networking
90
95
  - **`GCP.Network`** — VPC network.
91
96
  - **`GCP.Subnetwork`** — VPC subnetwork.
92
97
  - **`GCP.PsaConnection`** — Private Service Access peering for Google managed services.
93
98
  - **`GCP.GlobalAddress`** — global IP address (typically used to reserve a PSA range).
94
99
  - **`GCP.SharedVpcHost`** / **`GCP.SharedVpcServiceProject`** — Shared VPC enable/attach.
95
- - **`GCP.ParallelstoreInstance`** — Parallelstore filesystem instance.
100
+
101
+ ### Serverless (Cloud Run v2)
102
+ - **`GCP.Service`** — Cloud Run HTTP service with traffic routing, ingress controls, and IAM.
103
+ - **`GCP.Job`** — Cloud Run batch workload (declarative definition; trigger execution imperatively via `runProjectsLocationsJobs`).
104
+ - **`GCP.serviceIamMember`** / **`GCP.jobIamMember`** — bind `(role, member)` IAM grants on Cloud Run resources. Provider unions all bindings and writes one `setIamPolicy` per target, preserving foreign roles.
105
+
106
+ ### Storage
107
+ - **`GCP.ManagedLustreInstance`** — Managed Lustre filesystem instance.
96
108
 
97
109
  Each resource's full prop/attribute set is documented as JSDoc on the source.
98
110
 
111
+ ### Cloud Run example
112
+
113
+ ```ts
114
+ const api = yield* GCP.Service("HelloApi", {
115
+ project: project.projectId,
116
+ location: "europe-west4",
117
+ template: {
118
+ containers: [{ image: "gcr.io/cloudrun/hello" }],
119
+ },
120
+ });
121
+
122
+ // Declarative public access — preferred over `invokerIamDisabled: true`,
123
+ // which bypasses IAM entirely and leaves no audit trail.
124
+ yield* GCP.serviceIamMember(api, "PublicInvoker", {
125
+ role: "roles/run.invoker",
126
+ member: "allUsers",
127
+ });
128
+
129
+ // Batch workload — definition only; execution is imperative.
130
+ const batch = yield* GCP.Job("ProcessBatch", {
131
+ project: project.projectId,
132
+ location: "europe-west4",
133
+ template: {
134
+ taskCount: 1,
135
+ template: {
136
+ maxRetries: 3,
137
+ containers: [{ image: "europe-west4-docker.pkg.dev/proj/repo/worker:v1" }],
138
+ },
139
+ },
140
+ });
141
+ ```
142
+
143
+ Clean type aliases are re-exported from the top level so consumers don't need to import from `@distilled.cloud/gcp` directly: `GCP.RevisionTemplate`, `GCP.Container`, `GCP.TrafficTarget`, `GCP.VpcAccess`, `GCP.NodeSelector`, etc.
144
+
99
145
  ## Adoption
100
146
 
101
147
  `read` is gated on the alchemy internal labels `alchemy_app` / `alchemy_stage` / `alchemy_id`. Existing GCP resources lacking those labels are returned `Unowned` — the engine refuses to take them over without explicit `--adopt` (or `adopt(true)` on the resource call).
102
148
 
149
+ For resources that don't carry labels (Network, Subnetwork), the same `(app, stage, id)` triple is encoded as a sentinel inside `description` and matched on `read`.
150
+
151
+ ## Optimistic concurrency
152
+
153
+ Resources with server-side fingerprints (Cloud Run Service/Job, Subnetwork) pass the observed `etag`/`fingerprint` through patch and delete calls. A concurrent edit between observe and patch surfaces as `Conflict` rather than silently overwriting.
154
+
103
155
  ## License
104
156
 
105
157
  Apache-2.0.
@@ -0,0 +1,47 @@
1
+ import { ConfigError } from "@distilled.cloud/gcp";
2
+ import * as ar from "@distilled.cloud/gcp/artifactregistry-v1";
3
+ import type { ScopedPlanStatusSession } from "alchemy/Cli/Cli";
4
+ import * as Effect from "effect/Effect";
5
+ /**
6
+ * Resolved callable signature of `ar.getProjectsLocationsOperations`.
7
+ *
8
+ * `ar.getProjectsLocationsOperations` is itself an
9
+ * `Effect<callable, never, Credentials | HttpClient>`, so
10
+ * `Effect.Success` extracts the callable type. Using a derived alias
11
+ * keeps the helper aligned with the SDK without us hand-writing the
12
+ * input/output/error union, which would drift if the patch set changes.
13
+ */
14
+ type GetOperations = Effect.Success<typeof ar.getProjectsLocationsOperations>;
15
+ /**
16
+ * Build the Artifact Registry long-running-operation polling helper.
17
+ *
18
+ * Artifact Registry `Operation`s use the standard
19
+ * `GoogleLongrunningOperation` shape — doneness is `.done === true`
20
+ * (NOT `.status === "DONE"` like the GKE Container API and NOT
21
+ * Sqladmin's tri-state `.status`). Verified at
22
+ * `vendor/distilled/packages/gcp/src/services/artifactregistry-v1.ts:308–328`.
23
+ *
24
+ * Operation names returned by `artifactregistry-v1`
25
+ * (`createProjectsLocationsRepositories`,
26
+ * `patchProjectsLocationsRepositories`,
27
+ * `deleteProjectsLocationsRepositories`) are already fully qualified
28
+ * (`projects/{p}/locations/{l}/operations/{id}`), so callers pass them
29
+ * straight through to `getProjectsLocationsOperations` — no qualify
30
+ * dance needed (unlike Container, which returns bare ids).
31
+ *
32
+ * Schedule: exponential 1s → 1.5× growth, capped per-poll at 10s via
33
+ * `Schedule.either`, then a hard count cap of 60 retries → ~10 min wall
34
+ * ceiling. Repository CRUD is fast (seconds); the only long-running
35
+ * case is a delete on a repository with many cached images, where AR
36
+ * has to GC the underlying blobs.
37
+ *
38
+ * The helper is a factory — the caller resolves
39
+ * `ar.getProjectsLocationsOperations` once at provider construction
40
+ * and passes the resulting callable here, so we never re-resolve
41
+ * services on each await.
42
+ */
43
+ export declare const makeAwaitOperation: (getOperations: GetOperations) => (operationName: string, session: ScopedPlanStatusSession) => Effect.Effect<ar.Operation, ar.GetProjectsLocationsOperationsError | ConfigError | {
44
+ _tag: "OperationPending";
45
+ }, never>;
46
+ export {};
47
+ //# sourceMappingURL=Operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Operations.d.ts","sourceRoot":"","sources":["../../src/ArtifactRegistry/Operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC;;;;;;;;GAQG;AACH,KAAK,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,8BAA8B,CAAC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,kBAAkB,GAAI,eAAe,aAAa;;SAgC3D,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { ConfigError } from "@distilled.cloud/gcp";
2
+ import * as Duration from "effect/Duration";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Schedule from "effect/Schedule";
5
+ /**
6
+ * Build the Artifact Registry long-running-operation polling helper.
7
+ *
8
+ * Artifact Registry `Operation`s use the standard
9
+ * `GoogleLongrunningOperation` shape — doneness is `.done === true`
10
+ * (NOT `.status === "DONE"` like the GKE Container API and NOT
11
+ * Sqladmin's tri-state `.status`). Verified at
12
+ * `vendor/distilled/packages/gcp/src/services/artifactregistry-v1.ts:308–328`.
13
+ *
14
+ * Operation names returned by `artifactregistry-v1`
15
+ * (`createProjectsLocationsRepositories`,
16
+ * `patchProjectsLocationsRepositories`,
17
+ * `deleteProjectsLocationsRepositories`) are already fully qualified
18
+ * (`projects/{p}/locations/{l}/operations/{id}`), so callers pass them
19
+ * straight through to `getProjectsLocationsOperations` — no qualify
20
+ * dance needed (unlike Container, which returns bare ids).
21
+ *
22
+ * Schedule: exponential 1s → 1.5× growth, capped per-poll at 10s via
23
+ * `Schedule.either`, then a hard count cap of 60 retries → ~10 min wall
24
+ * ceiling. Repository CRUD is fast (seconds); the only long-running
25
+ * case is a delete on a repository with many cached images, where AR
26
+ * has to GC the underlying blobs.
27
+ *
28
+ * The helper is a factory — the caller resolves
29
+ * `ar.getProjectsLocationsOperations` once at provider construction
30
+ * and passes the resulting callable here, so we never re-resolve
31
+ * services on each await.
32
+ */
33
+ export const makeAwaitOperation = (getOperations) => Effect.fn(function* (operationName, session) {
34
+ const op = yield* getOperations({ name: operationName }).pipe(Effect.flatMap((current) => current.done === true
35
+ ? Effect.succeed(current)
36
+ : Effect.fail({ _tag: "OperationPending" })), Effect.retry({
37
+ while: (e) => e?._tag === "OperationPending",
38
+ schedule: Schedule.exponential(Duration.seconds(1), 1.5).pipe(Schedule.either(Schedule.spaced(Duration.seconds(10))), Schedule.both(Schedule.recurs(60)), Schedule.tapOutput(() => session.note(`Waiting for Artifact Registry operation ${operationName}…`))),
39
+ }));
40
+ if (op.error) {
41
+ return yield* new ConfigError({
42
+ message: `Artifact Registry operation ${operationName} failed: ${op.error.message ?? JSON.stringify(op.error)}`,
43
+ });
44
+ }
45
+ return op;
46
+ });
47
+ //# sourceMappingURL=Operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Operations.js","sourceRoot":"","sources":["../../src/ArtifactRegistry/Operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAa5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,aAA4B,EAAE,EAAE,CACjE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EACjB,aAAqB,EACrB,OAAgC;IAEhC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,IAAI,CAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CACzB,OAAO,CAAC,IAAI,KAAK,IAAI;QACnB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QACzB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAA2B,EAAE,CAAC,CACvD,EACD,MAAM,CAAC,KAAK,CAAC;QACX,KAAK,EAAE,CAAC,CAAoB,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,kBAAkB;QAC/D,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAC3D,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EACtD,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAClC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,CACtB,OAAO,CAAC,IAAI,CACV,2CAA2C,aAAa,GAAG,CAC5D,CACF,CACF;KACF,CAAC,CACH,CAAC;IACF,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,KAAK,CAAC,CAAC,IAAI,WAAW,CAAC;YAC5B,OAAO,EAAE,+BAA+B,aAAa,YACnD,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAC7C,EAAE;SACH,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,263 @@
1
+ import * as ar from "@distilled.cloud/gcp/artifactregistry-v1";
2
+ import { Resource } from "alchemy";
3
+ import * as Provider from "alchemy/Provider";
4
+ import type * as GCP from "../Providers.ts";
5
+ import type { ArtifactRegistryFormat, ArtifactRegistryMode } from "./Types.ts";
6
+ /**
7
+ * Docker remote upstream — either a Google-known public repository
8
+ * (today: Docker Hub) or an arbitrary HTTPS Docker registry like
9
+ * `https://nvcr.io` or `https://quay.io`. Discriminated union — the
10
+ * server rejects bodies that supply both. Matches
11
+ * `ar.DockerRepository`.
12
+ *
13
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#DockerRepository
14
+ */
15
+ export type ArtifactRegistryDockerUpstream = {
16
+ /**
17
+ * One of Artifact Registry's built-in public-repository
18
+ * presets. Today only `DOCKER_HUB` is meaningful for the docker
19
+ * format. The string-tail is left open for forward compat with
20
+ * any future presets the server adds.
21
+ */
22
+ publicRepository: "DOCKER_HUB" | (string & {});
23
+ customRepository?: never;
24
+ } | {
25
+ /**
26
+ * Arbitrary HTTPS Docker upstream — `https://nvcr.io`,
27
+ * `https://quay.io`, a self-hosted Harbor, etc. The server
28
+ * trims any path / pulls the manifest endpoint conventionally.
29
+ */
30
+ customRepository: {
31
+ uri: string;
32
+ };
33
+ publicRepository?: never;
34
+ };
35
+ /**
36
+ * A Google Artifact Registry **Repository** of `format: "DOCKER"` —
37
+ * either a standard upload-target (`STANDARD_REPOSITORY`) for the
38
+ * project's own container builds, or a pull-through cache
39
+ * (`REMOTE_REPOSITORY`) pointing at Docker Hub or a custom upstream
40
+ * like `nvcr.io`.
41
+ *
42
+ * **Resource model.** A repository lives at
43
+ * `projects/{project}/locations/{location}/repositories/{name}`.
44
+ * Format and mode are immutable; the upstream config for a remote
45
+ * repository is also immutable on the GCP side (any change triggers a
46
+ * replace).
47
+ *
48
+ * **Lifecycle.** observe → ensure (create LRO; retry on the
49
+ * "API not yet enabled" 403 propagation race) → sync (patch
50
+ * description, labels, dockerConfig, cleanup policies via
51
+ * `updateMask`) → return. Mirrors `Sqladmin/Instance.ts` /
52
+ * `Run/Service.ts`.
53
+ *
54
+ * **Replace triggers.** `project`, `location`, `name`, `format`,
55
+ * `mode`, `kmsKeyName`, and any change inside `remoteRepositoryConfig`.
56
+ *
57
+ * **Adoption.** Label-gated via `alchemy_*` labels. A repository
58
+ * whose `labels` lack our keys is wrapped in `Unowned(attrs)` from
59
+ * `read`, forcing `--adopt` before takeover.
60
+ *
61
+ * @example Standard Docker repo for the project's own builds
62
+ * ```typescript
63
+ * const apps = yield* GCP.ArtifactRegistryRepository("Apps", {
64
+ * project: project.projectId,
65
+ * location: "europe-west4",
66
+ * format: "DOCKER",
67
+ * description: "App images built by CI",
68
+ * });
69
+ * ```
70
+ *
71
+ * @example Pull-through cache for Docker Hub
72
+ * ```typescript
73
+ * yield* GCP.ArtifactRegistryRepository("DockerHubMirror", {
74
+ * project: project.projectId,
75
+ * location: "europe-west4",
76
+ * format: "DOCKER",
77
+ * mode: "REMOTE_REPOSITORY",
78
+ * remoteRepositoryConfig: {
79
+ * description: "Pull-through cache for docker.io",
80
+ * dockerRepository: { publicRepository: "DOCKER_HUB" },
81
+ * },
82
+ * });
83
+ * ```
84
+ *
85
+ * @example Pull-through cache for nvcr.io
86
+ * ```typescript
87
+ * yield* GCP.ArtifactRegistryRepository("NvcrMirror", {
88
+ * project: project.projectId,
89
+ * location: "europe-west4",
90
+ * format: "DOCKER",
91
+ * mode: "REMOTE_REPOSITORY",
92
+ * remoteRepositoryConfig: {
93
+ * description: "Pull-through cache for nvcr.io",
94
+ * dockerRepository: { customRepository: { uri: "https://nvcr.io" } },
95
+ * },
96
+ * });
97
+ * ```
98
+ *
99
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Repository
100
+ */
101
+ export type ArtifactRegistryRepositoryProps = {
102
+ /**
103
+ * GCP project ID hosting the repository. Immutable — replace if
104
+ * changed.
105
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Repository
106
+ */
107
+ project: string;
108
+ /**
109
+ * Artifact Registry location (e.g. `europe-west4`). Regional only —
110
+ * multi-region is not supported by this resource. Immutable.
111
+ * @see https://cloud.google.com/artifact-registry/docs/repositories/repo-locations
112
+ */
113
+ location: string;
114
+ /**
115
+ * Repository short name (the trailing path segment under
116
+ * `…/repositories/`). Defaults to a sanitised
117
+ * `createPhysicalName({ id, lowercase: true, maxLength: 63 })`.
118
+ * Lowercase letters/digits/hyphens; start with a letter or digit;
119
+ * 1–63 chars. Immutable — replace if changed.
120
+ * @see https://cloud.google.com/artifact-registry/docs/repositories/create-repos#create
121
+ */
122
+ name?: string;
123
+ /**
124
+ * Package format. Today the provider exercises only `DOCKER`; other
125
+ * formats are reachable via the open-tail union but un-tested.
126
+ * Immutable — replace if changed.
127
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Format
128
+ */
129
+ format: ArtifactRegistryFormat;
130
+ /**
131
+ * Repository mode. Defaults to `STANDARD_REPOSITORY`. Set to
132
+ * `REMOTE_REPOSITORY` to act as a pull-through cache for an upstream
133
+ * Docker registry; in that case `remoteRepositoryConfig` is
134
+ * required. Immutable — replace if changed (GCP does not allow
135
+ * flipping a repository between modes).
136
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Mode
137
+ */
138
+ mode?: ArtifactRegistryMode;
139
+ /**
140
+ * Human-readable description. Mutable via `patch` with
141
+ * `updateMask=description`.
142
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Repository.FIELDS.description
143
+ */
144
+ description?: string;
145
+ /**
146
+ * Resource labels. Alchemy internal labels (`alchemy_app`,
147
+ * `alchemy_stage`, `alchemy_id`) are merged on top automatically.
148
+ * Mutable via `patch` with `updateMask=labels`.
149
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#Repository.FIELDS.labels
150
+ */
151
+ labels?: Record<string, string>;
152
+ /**
153
+ * Customer-managed encryption key, fully-qualified
154
+ * (`projects/{p}/locations/{l}/keyRings/{kr}/cryptoKeys/{k}`).
155
+ * Immutable on the GCP side — we surface that as a replace trigger.
156
+ * Requires the AR service agent to hold
157
+ * `roles/cloudkms.cryptoKeyEncrypterDecrypter` on the key.
158
+ * @see https://cloud.google.com/artifact-registry/docs/cmek
159
+ */
160
+ kmsKeyName?: string;
161
+ /**
162
+ * Docker-format repository configuration. Mutable via `patch` with
163
+ * `updateMask=dockerConfig`.
164
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#DockerRepositoryConfig
165
+ */
166
+ dockerConfig?: {
167
+ /**
168
+ * When true, image tags inside this repository cannot be moved or
169
+ * deleted once written. Useful for supply-chain hardening.
170
+ */
171
+ immutableTags?: boolean;
172
+ };
173
+ /**
174
+ * Remote-repository configuration. Required (and only meaningful)
175
+ * when `mode === "REMOTE_REPOSITORY"`. Immutable on the GCP side —
176
+ * any change to fields under this object triggers a replace.
177
+ * @see https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories#RemoteRepositoryConfig
178
+ */
179
+ remoteRepositoryConfig?: {
180
+ /**
181
+ * Description of the remote source (distinct from the top-level
182
+ * `description`).
183
+ */
184
+ description?: string;
185
+ /** Docker upstream — public preset or arbitrary HTTPS URI. */
186
+ dockerRepository?: ArtifactRegistryDockerUpstream;
187
+ /**
188
+ * If true, AR skips the create-time HEAD request against the
189
+ * upstream. Useful for upstreams that gate unauthenticated probes
190
+ * (private nvcr.io paths, etc.).
191
+ */
192
+ disableUpstreamValidation?: boolean;
193
+ /**
194
+ * Credentials for authenticated upstreams — supply a Secret
195
+ * Manager version reference for the password rather than the
196
+ * cleartext value.
197
+ */
198
+ upstreamCredentials?: {
199
+ usernamePasswordCredentials?: {
200
+ username?: string;
201
+ /**
202
+ * Secret Manager secret version path:
203
+ * `projects/{p}/secrets/{s}/versions/{v}`.
204
+ */
205
+ passwordSecretVersion?: string;
206
+ };
207
+ };
208
+ };
209
+ /**
210
+ * Cleanup policies — map of policy ID → policy spec. Pass-through
211
+ * to the API today; the policy types are accepted via the
212
+ * distilled SDK's `CleanupPolicy` shape but not specifically
213
+ * validated by this provider. Mutable via `patch` with
214
+ * `updateMask=cleanupPolicies`.
215
+ * @see https://cloud.google.com/artifact-registry/docs/repositories/cleanup-policy
216
+ */
217
+ cleanupPolicies?: Record<string, ar.CleanupPolicy>;
218
+ /**
219
+ * If true, cleanup policies are evaluated and reported but no
220
+ * versions are actually deleted. Mutable via `patch` with
221
+ * `updateMask=cleanupPolicyDryRun`.
222
+ * @see https://cloud.google.com/artifact-registry/docs/repositories/cleanup-policy#test
223
+ */
224
+ cleanupPolicyDryRun?: boolean;
225
+ };
226
+ export type ArtifactRegistryRepositoryAttributes = {
227
+ /** Bare repository name (no `projects/.../repositories/` prefix). */
228
+ name: string;
229
+ /**
230
+ * Server-side fully-qualified name:
231
+ * `projects/{project}/locations/{location}/repositories/{name}`.
232
+ */
233
+ fullyQualifiedName: string;
234
+ /** GCP project ID. */
235
+ project: string;
236
+ /** Region. */
237
+ location: string;
238
+ /** Echoed format (e.g. `DOCKER`). */
239
+ format: string;
240
+ /** Echoed mode (e.g. `STANDARD_REPOSITORY`). */
241
+ mode: string;
242
+ /**
243
+ * Repository endpoint (e.g. `europe-west4-docker.pkg.dev/proj/repo`)
244
+ * — the host:path you'd `docker pull` from. Populated by the server.
245
+ */
246
+ registryUri: string | undefined;
247
+ /** Total stored size in bytes, server-reported. */
248
+ sizeBytes: string | undefined;
249
+ /** PZS compliance flag. */
250
+ satisfiesPzs: boolean | undefined;
251
+ /** PZI compliance flag. */
252
+ satisfiesPzi: boolean | undefined;
253
+ /** Labels currently set, including alchemy internals. */
254
+ labels: Record<string, string>;
255
+ /** RFC3339 create timestamp. */
256
+ createTime: string;
257
+ /** RFC3339 last-update timestamp. */
258
+ updateTime: string;
259
+ };
260
+ export type ArtifactRegistryRepository = Resource<"GCP.ArtifactRegistryRepository", ArtifactRegistryRepositoryProps, ArtifactRegistryRepositoryAttributes, never, GCP.Providers>;
261
+ export declare const ArtifactRegistryRepository: import("alchemy").ResourceClass<ArtifactRegistryRepository>;
262
+ export declare const ArtifactRegistryRepositoryProvider: () => import("effect/Layer").Layer<Provider.Provider<ArtifactRegistryRepository>, never, import("effect/unstable/http/HttpClient").HttpClient | import("alchemy").Stage | import("alchemy").Stack | import("@distilled.cloud/gcp").Credentials>;
263
+ //# sourceMappingURL=Repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Repository.d.ts","sourceRoot":"","sources":["../../src/ArtifactRegistry/Repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,0CAA0C,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAK7C,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAG5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAO/E;;;;;;;;GAQG;AACH,MAAM,MAAM,8BAA8B,GACtC;IACE;;;;;OAKG;IACH,gBAAgB,EAAE,YAAY,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,KAAK,CAAC;CAC1B,GACD;IACE;;;;OAIG;IACH,gBAAgB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,KAAK,CAAC;CAC1B,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,MAAM,EAAE,sBAAsB,CAAC;IAC/B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE;QACb;;;WAGG;QACH,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;IACF;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE;QACvB;;;WAGG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,8DAA8D;QAC9D,gBAAgB,CAAC,EAAE,8BAA8B,CAAC;QAClD;;;;WAIG;QACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;QACpC;;;;WAIG;QACH,mBAAmB,CAAC,EAAE;YACpB,2BAA2B,CAAC,EAAE;gBAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAClB;;;mBAGG;gBACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;aAChC,CAAC;SACH,CAAC;KACH,CAAC;IACF;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC;IACnD;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,mDAAmD;IACnD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,2BAA2B;IAC3B,YAAY,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,2BAA2B;IAC3B,YAAY,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAC/C,gCAAgC,EAChC,+BAA+B,EAC/B,oCAAoC,EACpC,KAAK,EACL,GAAG,CAAC,SAAS,CACd,CAAC;AACF,eAAO,MAAM,0BAA0B,6DACiC,CAAC;AAwHzE,eAAO,MAAM,kCAAkC,iPAyN5C,CAAC"}