@prisma/composer-prisma-cloud 0.6.0-dev.20 → 0.6.0-dev.22

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.
@@ -10,7 +10,7 @@ import { Resource } from "alchemy";
10
10
  import "effect/Schedule";
11
11
  import * as Output from "alchemy/Output";
12
12
  import { ExtensionDescriptor, StateDescriptor } from "@prisma/composer/config";
13
- //#region ../../1-prisma-cloud/0-lowering/lowering/dist/compute-8Sq4NWg1.d.mts
13
+ //#region ../../1-prisma-cloud/0-lowering/lowering/dist/compute-CW3HkgJe.d.mts
14
14
  /** Every region Prisma Compute serves — the runtime source of truth; `ComputeRegion` is derived from it so the two can never drift. */
15
15
  declare const COMPUTE_REGIONS: readonly ["us-east-1", "us-west-1", "eu-west-3", "eu-central-1", "ap-northeast-1", "ap-southeast-1"];
16
16
  type ComputeRegion = (typeof COMPUTE_REGIONS)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.mts","names":[],"sources":["../../../1-prisma-cloud/0-lowering/lowering/dist/compute-8Sq4NWg1.d.mts","../../../1-prisma-cloud/1-extensions/target/dist/control.d.mts"],"mappings":";;;;;;;;;;;;;;cA4Dc;KACT,wBAAwB;;;;;;;;;;;;;;;;;;;UCzCnB,sBAAsB;;;;;;;;WAQrB,QAAQ,6BAA6B,OAAO;;;;;;;;UAQ7C;WACC,gBAAgB,OAAO;;;;;;;;;;;;UAYxB,6BAA6B;WAC5B,kBAAkB,aAAa,8BAA8B,oBAAoB,OAAO;;;;;cAKrF,mBAAmB;UACvB;;EAER;;EAEA,SAAS;;;;;;;;KAQN,qBAAqB,KAAK,0BAA0B,KAAK;;;;;;;;;;iBAU7C,oBAAoB,kBAAkB,sBAAsB,QAAQ,oBAAoB,sBAAsB,oBAAoB,gBAAgB;cACrJ,iBAAiB,oBAAoB,gBAAgB;;cAErD,cAAc,OAAO,uBAAuB"}
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../../../1-prisma-cloud/0-lowering/lowering/dist/compute-CW3HkgJe.d.mts","../../../1-prisma-cloud/1-extensions/target/dist/control.d.mts"],"mappings":";;;;;;;;;;;;;;cA4Dc;KACT,wBAAwB;;;;;;;;;;;;;;;;;;;UCzCnB,sBAAsB;;;;;;;;WAQrB,QAAQ,6BAA6B,OAAO;;;;;;;;UAQ7C;WACC,gBAAgB,OAAO;;;;;;;;;;;;UAYxB,6BAA6B;WAC5B,kBAAkB,aAAa,8BAA8B,oBAAoB,OAAO;;;;;cAKrF,mBAAmB;UACvB;;EAER;;EAEA,SAAS;;;;;;;;KAQN,qBAAqB,KAAK,0BAA0B,KAAK;;;;;;;;;;iBAU7C,oBAAoB,kBAAkB,sBAAsB,QAAQ,oBAAoB,sBAAsB,oBAAoB,gBAAgB;cACrJ,iBAAiB,oBAAoB,gBAAgB;;cAErD,cAAc,OAAO,uBAAuB"}
package/dist/control.mjs CHANGED
@@ -2,11 +2,12 @@ import { o as paramBindingFor, s as paramName } from "./secret-Doubjztg-CWHCDiuX
2
2
  import { a as encodeParamPointer, i as encode, l as serializeInput, n as configKey, o as paramEntries } from "./serializer-Ch1Tmx-2-D7cNhkEp.mjs";
3
3
  import { a as provisionedEdges, n as SELF_ORIGIN, r as STREAMS_API_KEY, t as RESERVED_PROVIDER_PARAMS } from "./provisioned-edges-COr17uxy-DymAGn_V.mjs";
4
4
  import { n as requiredPackHeadOf } from "./required-pack-head-BrrJarzc-DeP50pBu.mjs";
5
- import { A as packageComputeArtifact, B as call, D as EnvironmentVariable, E as Deployment, F as resolveDefaultBranchId, H as layer, I as MANAGEMENT_API_ORIGIN, L as ManagementClient, M as BucketKey, N as collectPages, O as ServiceKey, P as drivePagesAsync, R as PrismaApiError, S as Database, T as ComputeService, V as fromEnv, _ as resolveTargetRef, a as PgWarmProvider, b as providers, d as collectPreflightNames, f as containerDescriptor, g as resolvePrismaNextConfig, h as prismaCloudContainerOf, i as PgWarm, j as Bucket, k as ServiceKeyProvider, l as S3Credentials, m as packHeadRefHashes, n as GeneratedParamProvider, o as PnMigration, r as PRISMA_CLOUD_EXTENSION_ID, s as PnMigrationProvider, t as GeneratedParam, u as S3CredentialsProvider, w as COMPUTE_REGIONS, x as Connection, z as PrismaCredentials } from "./s3-credentials-resource-BltGd-gG-B_bNhtGi.mjs";
5
+ import { A as packageComputeArtifact, B as PrismaCredentials, D as EnvironmentVariable, E as Deployment, F as resolveDefaultBranchId, H as layer, I as PrismaApiError, L as call, M as BucketKey, N as collectPages, O as ServiceKey, P as drivePagesAsync, R as MANAGEMENT_API_ORIGIN, S as Database, T as ComputeService, V as fromEnv, _ as resolveTargetRef, a as PgWarmProvider, b as providers, d as collectPreflightNames, f as containerDescriptor, g as resolvePrismaNextConfig, h as prismaCloudContainerOf, i as PgWarm, j as Bucket, k as ServiceKeyProvider, l as S3Credentials, m as packHeadRefHashes, n as GeneratedParamProvider, o as PnMigration, r as PRISMA_CLOUD_EXTENSION_ID, s as PnMigrationProvider, t as GeneratedParam, u as S3CredentialsProvider, w as COMPUTE_REGIONS, x as Connection, z as ManagementClient } from "./s3-credentials-resource-BltGd-gG-2K8Cgwoi.mjs";
6
6
  import { n as isPnPostgresResourceNode } from "./prisma-next-hB3CQxKI-BYVc-k-F.mjs";
7
7
  import { isParamSource } from "@prisma/composer";
8
8
  import { blindCast } from "@prisma/composer/casts";
9
9
  import { RPC_PEER_KEY } from "@prisma/composer/service-rpc";
10
+ import { createManagementApiClient } from "@prisma/management-api-sdk";
10
11
  import * as Effect from "effect/Effect";
11
12
  import * as Layer from "effect/Layer";
12
13
  import * as Redacted from "effect/Redacted";
@@ -18,8 +19,208 @@ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
18
19
  import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
19
20
  import * as Headers from "effect/unstable/http/Headers";
20
21
  import * as Output from "alchemy/Output";
22
+ import { execFileSync } from "node:child_process";
23
+ //#region ../../1-prisma-cloud/0-lowering/lowering/dist/resources-rlwsFefZ.mjs
24
+ /**
25
+ * Reporting is observability, so no call may stall the deploy: every request
26
+ * carries this deadline, and an expired one is warned and dropped like any
27
+ * other failure.
28
+ */
29
+ const REPORT_DEADLINE_MS = 1e4;
30
+ function buildsApi(options) {
31
+ const { client, warn } = options;
32
+ /** Runs one call, turning every failure — transport or refusal — into a warning and `undefined`. */
33
+ const send = async (call, describe) => {
34
+ let result;
35
+ try {
36
+ result = await call();
37
+ } catch (error) {
38
+ const detail = error instanceof Error ? error.message : String(error);
39
+ warn(`Could not reach Prisma Cloud to ${describe}: ${detail}`);
40
+ return;
41
+ }
42
+ if (!result.response.ok) {
43
+ const detail = result.error === void 0 ? "" : `: ${JSON.stringify(result.error)}`;
44
+ warn(`Prisma Cloud refused to ${describe} (HTTP ${String(result.response.status)})${detail}`);
45
+ return;
46
+ }
47
+ return result.data ?? {};
48
+ };
49
+ return {
50
+ async create(body) {
51
+ const created = await send(() => client.POST("/v1/builds", {
52
+ body,
53
+ signal: AbortSignal.timeout(REPORT_DEADLINE_MS)
54
+ }), "record this deploy");
55
+ if (created === void 0) return void 0;
56
+ const id = created.data?.id;
57
+ if (id === void 0 || id.length === 0) {
58
+ warn("Prisma Cloud recorded this deploy but returned no build id.");
59
+ return;
60
+ }
61
+ return id;
62
+ },
63
+ async update(id, body) {
64
+ return await send(() => client.PATCH("/v1/builds/{buildId}", {
65
+ params: { path: { buildId: id } },
66
+ body,
67
+ signal: AbortSignal.timeout(REPORT_DEADLINE_MS)
68
+ }), "update this deploy's build") !== void 0;
69
+ },
70
+ async reportResource(id, resourceType, resourceId, action) {
71
+ return await send(() => client.PUT("/v1/builds/{buildId}/resources/{resourceType}/{resourceId}", {
72
+ params: { path: {
73
+ buildId: id,
74
+ resourceType,
75
+ resourceId
76
+ } },
77
+ body: { action },
78
+ signal: AbortSignal.timeout(REPORT_DEADLINE_MS)
79
+ }), `record the ${resourceType} this deploy ${action === "acted_on" ? "acted on" : action}`) !== void 0;
80
+ }
81
+ };
82
+ }
83
+ /** Names the build a run belongs to. Set by the CLI on the alchemy child, or by a CI runner that created the build itself. */
84
+ const BUILD_ID_ENV = "PRISMA_BUILD_ID";
85
+ /**
86
+ * Alchemy resource type → platform resource type.
87
+ *
88
+ * Two of this package's resources are deliberately absent. `Prisma.BucketKey`
89
+ * has no platform resource type to map onto. `PrismaCloud.ServiceKey` is a
90
+ * value this deploy mints locally, not a platform resource at all — the
91
+ * platform's `service_key` is what `Prisma.Connection` creates.
92
+ *
93
+ * Anything not listed — `PgWarm`, and every resource another extension
94
+ * contributes — is not a Prisma Cloud resource and is not reported.
95
+ */
96
+ const PLATFORM_RESOURCES = {
97
+ "Prisma.Project": {
98
+ type: "project",
99
+ idField: "id"
100
+ },
101
+ "Prisma.Database": {
102
+ type: "database",
103
+ idField: "id"
104
+ },
105
+ "Prisma.Connection": {
106
+ type: "service_key",
107
+ idField: "id"
108
+ },
109
+ "Prisma.Bucket": {
110
+ type: "bucket",
111
+ idField: "id"
112
+ },
113
+ "Prisma.ComputeService": {
114
+ type: "app",
115
+ idField: "id"
116
+ },
117
+ "Prisma.Deployment": {
118
+ type: "deployment",
119
+ idField: "deploymentId"
120
+ },
121
+ "Prisma.EnvironmentVariable": {
122
+ type: "config_variable",
123
+ idField: "id"
124
+ }
125
+ };
126
+ /**
127
+ * Terminal status → what the run did to the resource. The intermediate
128
+ * statuses (`creating`, `updating`, `replacing`) are skipped: they say work
129
+ * started, not that it landed, and the terminal write follows immediately.
130
+ *
131
+ * `updated` maps to `acted_on` rather than to nothing, because a reconcile
132
+ * that changed no field is still this run acting on that resource — a
133
+ * migration against an untouched database is an action on it.
134
+ */
135
+ const ACTION_BY_STATUS = {
136
+ created: "created",
137
+ updated: "acted_on",
138
+ deleting: "deleted"
139
+ };
140
+ const isRecord = (value) => typeof value === "object" && value !== null;
141
+ /**
142
+ * What a persisted state record says this run did, or `undefined` when it
143
+ * says nothing reportable. Reads defensively: the record crosses a wire and
144
+ * carries resources from every extension, not only this one's.
145
+ */
146
+ function reportableResource(value) {
147
+ if (!isRecord(value)) return void 0;
148
+ if (value["kind"] === "action") return void 0;
149
+ if (value["adopting"] === true) return void 0;
150
+ const resourceType = value["resourceType"];
151
+ if (typeof resourceType !== "string") return void 0;
152
+ const mapping = PLATFORM_RESOURCES[resourceType];
153
+ if (mapping === void 0) return void 0;
154
+ const status = value["status"];
155
+ const action = typeof status === "string" ? ACTION_BY_STATUS[status] : void 0;
156
+ if (action === void 0) return void 0;
157
+ const attr = value["attr"];
158
+ if (!isRecord(attr)) return void 0;
159
+ const id = attr[mapping.idField];
160
+ if (typeof id !== "string" || id.length === 0) return void 0;
161
+ return {
162
+ type: mapping.type,
163
+ id,
164
+ action
165
+ };
166
+ }
167
+ /**
168
+ * The most a drain will wait, total. Every real report already carries the
169
+ * api layer's per-request deadline, so this never fires for the shipped
170
+ * `BuildsApi`; it is the backstop that keeps the state layer's finalizer —
171
+ * and therefore the deploy lease release — bounded against any implementation.
172
+ */
173
+ const DRAIN_DEADLINE_MS = 15e3;
174
+ function resourceReporter(api, buildId, warn = (message) => console.warn(message), drainDeadlineMs = DRAIN_DEADLINE_MS) {
175
+ const inFlight = /* @__PURE__ */ new Set();
176
+ const reported = /* @__PURE__ */ new Set();
177
+ return {
178
+ observe(value) {
179
+ const resource = reportableResource(value);
180
+ if (resource === void 0) return;
181
+ const key = `${resource.type}:${resource.id}:${resource.action}`;
182
+ if (reported.has(key)) return;
183
+ reported.add(key);
184
+ const sent = api.reportResource(buildId, resource.type, resource.id, resource.action).finally(() => inFlight.delete(sent));
185
+ inFlight.add(sent);
186
+ },
187
+ async drain() {
188
+ const deadline = Date.now() + drainDeadlineMs;
189
+ while (inFlight.size > 0) {
190
+ const remaining = deadline - Date.now();
191
+ if (remaining <= 0) {
192
+ warn(`Abandoned ${inFlight.size} in-flight resource report(s) after ${drainDeadlineMs}ms.`);
193
+ return;
194
+ }
195
+ await Promise.race([Promise.allSettled([...inFlight]), new Promise((resolve) => setTimeout(resolve, remaining).unref?.())]);
196
+ if (Date.now() >= deadline && inFlight.size > 0) {
197
+ warn(`Abandoned ${inFlight.size} in-flight resource report(s) after ${drainDeadlineMs}ms.`);
198
+ return;
199
+ }
200
+ }
201
+ }
202
+ };
203
+ }
204
+ //#endregion
21
205
  //#region ../../1-prisma-cloud/0-lowering/lowering/dist/state.mjs
22
206
  /**
207
+ * Reports on the way out of a successful `set`, never before it: a write that
208
+ * failed leaves the resource unrecorded here, and claiming otherwise would
209
+ * make the build's provenance a guess.
210
+ */
211
+ function withResourceReporting(inner, api, buildId, warn) {
212
+ const reporter = resourceReporter(api, buildId, warn);
213
+ return {
214
+ store: {
215
+ ...inner,
216
+ set(request) {
217
+ return inner.set(request).pipe(Effect.tap(() => Effect.sync(() => reporter.observe(request.value))));
218
+ }
219
+ },
220
+ reporter
221
+ };
222
+ }
223
+ /**
23
224
  * Whether the platform state API holds any resources for (stack, stage).
24
225
  * Requires the live deploy lease — the listing runs under it, like every
25
226
  * state operation.
@@ -250,10 +451,217 @@ const stateLayerAgainst = (apiOrigin, ids) => {
250
451
  transformClient: (req) => HttpClientRequest.setHeader(req, LEASE_HEADER, Redacted.value(lease.leaseId)),
251
452
  id: "prisma-postgres"
252
453
  }).pipe(Effect.provide(FetchHttpClient.layer));
253
- return Effect.succeed(service);
454
+ const buildId = process.env[BUILD_ID_ENV];
455
+ if (buildId === void 0 || buildId.length === 0) return Effect.succeed(service);
456
+ const { store, reporter } = withResourceReporting(service, buildsApi({
457
+ client: mgmt,
458
+ warn: (message) => {
459
+ console.warn(message);
460
+ }
461
+ }), buildId);
462
+ yield* Effect.addFinalizer(() => Effect.promise(() => reporter.drain()));
463
+ return Effect.succeed(store);
254
464
  }).pipe(Effect.provide(dependencies))).pipe(Layer.orDie, Layer.merge(redactLeaseHeader));
255
465
  };
256
466
  //#endregion
467
+ //#region ../../1-prisma-cloud/0-lowering/lowering/dist/builds.mjs
468
+ /**
469
+ * Who is deploying, and from what commit — the identity a build is reported
470
+ * under.
471
+ *
472
+ * `commitSha` and `branchName` are required by the platform, and Composer has
473
+ * no other reason to read git, so this is the only place it does. A deploy
474
+ * from a directory that is not a git checkout has neither, and is reported
475
+ * not at all rather than reported with placeholder values: the Console keeps
476
+ * whatever it is told, and "unknown" would sit in a workspace's deploy
477
+ * history permanently.
478
+ */
479
+ const DIGITS = /^[0-9]+$/;
480
+ const nonEmpty$1 = (value) => value !== void 0 && value.length > 0 ? value : void 0;
481
+ function git(args, cwd) {
482
+ try {
483
+ const out = execFileSync("git", [...args], {
484
+ cwd,
485
+ encoding: "utf8",
486
+ stdio: [
487
+ "ignore",
488
+ "pipe",
489
+ "ignore"
490
+ ],
491
+ timeout: 5e3
492
+ });
493
+ return nonEmpty$1(out.trim());
494
+ } catch {
495
+ return;
496
+ }
497
+ }
498
+ /**
499
+ * The GitHub Actions run this is executing inside, when all three parts are
500
+ * present and are the digits the platform's dedup key requires. Partial or
501
+ * malformed input yields nothing rather than half an identity: the key joins
502
+ * its parts with `:`, so a part that is not digits could let two different
503
+ * runs spell the same key.
504
+ */
505
+ function githubRunIdentity(env) {
506
+ if (env["GITHUB_ACTIONS"] !== "true") return void 0;
507
+ const repositoryId = nonEmpty$1(env["GITHUB_REPOSITORY_ID"]);
508
+ const runId = nonEmpty$1(env["GITHUB_RUN_ID"]);
509
+ const attempt = nonEmpty$1(env["GITHUB_RUN_ATTEMPT"]) ?? "1";
510
+ if (repositoryId === void 0 || !DIGITS.test(repositoryId)) return void 0;
511
+ if (runId === void 0 || !DIGITS.test(runId)) return void 0;
512
+ if (!DIGITS.test(attempt)) return void 0;
513
+ const runAttempt = Number.parseInt(attempt, 10);
514
+ if (!Number.isInteger(runAttempt) || runAttempt < 1) return void 0;
515
+ return {
516
+ provider: "github",
517
+ repositoryId,
518
+ runId,
519
+ runAttempt
520
+ };
521
+ }
522
+ function githubRunUrl(env) {
523
+ const server = nonEmpty$1(env["GITHUB_SERVER_URL"]) ?? "https://github.com";
524
+ const repository = nonEmpty$1(env["GITHUB_REPOSITORY"]);
525
+ const runId = nonEmpty$1(env["GITHUB_RUN_ID"]);
526
+ if (repository === void 0 || runId === void 0) return void 0;
527
+ return `${server}/${repository}/actions/runs/${runId}/attempts/${nonEmpty$1(env["GITHUB_RUN_ATTEMPT"]) ?? "1"}`;
528
+ }
529
+ /**
530
+ * The branch this ran on. Inside a pull-request workflow `GITHUB_REF_NAME` is
531
+ * the synthetic merge ref (`123/merge`), so the head branch is preferred —
532
+ * it is the name a person would recognise in the Console.
533
+ */
534
+ function branchName(env, cwd) {
535
+ const fromEnv = nonEmpty$1(env["GITHUB_HEAD_REF"]) ?? nonEmpty$1(env["GITHUB_REF_NAME"]);
536
+ if (fromEnv !== void 0) return fromEnv;
537
+ const head = git([
538
+ "rev-parse",
539
+ "--abbrev-ref",
540
+ "HEAD"
541
+ ], cwd);
542
+ return head === "HEAD" ? void 0 : head;
543
+ }
544
+ /**
545
+ * The identity to report this run under, or `undefined` when there is not
546
+ * enough to report one honestly.
547
+ */
548
+ function resolveRunIdentity(cwd, env) {
549
+ const commitSha = nonEmpty$1(env["GITHUB_SHA"]) ?? git(["rev-parse", "HEAD"], cwd);
550
+ const branch = branchName(env, cwd);
551
+ if (commitSha === void 0 || branch === void 0) return void 0;
552
+ const runIdentity = githubRunIdentity(env);
553
+ return {
554
+ source: runIdentity === void 0 ? "cli" : "ci",
555
+ commitSha,
556
+ branchName: branch,
557
+ runIdentity,
558
+ externalLogUrl: runIdentity === void 0 ? void 0 : githubRunUrl(env)
559
+ };
560
+ }
561
+ /** An empty string is how a shell spells "unset", so it must not be mistaken for a build id. */
562
+ const nonEmpty = (value) => value !== void 0 && value.length > 0 ? value : void 0;
563
+ function buildReporter(options) {
564
+ return { begin: async (input) => {
565
+ try {
566
+ return await beginSession(input, options);
567
+ } catch (error) {
568
+ (options.warn ?? ((message) => console.warn(message)))(`Could not start build reporting: ${error instanceof Error ? error.message : String(error)}`);
569
+ return;
570
+ }
571
+ } };
572
+ }
573
+ async function beginSession(input, options) {
574
+ const env = options.env ?? process.env;
575
+ const warn = options.warn ?? ((message) => console.warn(message));
576
+ const injected = input.credentials?.client;
577
+ const token = env["PRISMA_SERVICE_TOKEN"];
578
+ if (options.api === void 0 && injected === void 0 && (token === void 0 || token.length === 0)) return;
579
+ const identity = resolveRunIdentity(input.cwd, env);
580
+ if (identity === void 0) {
581
+ warn(`\nNot recording this deploy in Prisma Cloud: ${input.cwd} has no commit and branch to report it under. Deploy from a git checkout to see it in the Console.`);
582
+ return;
583
+ }
584
+ const api = options.api ?? buildsApi({
585
+ client: injected ?? createManagementApiClient({
586
+ token: token ?? "",
587
+ baseUrl: options.origin ?? "https://api.prisma.io"
588
+ }),
589
+ warn
590
+ });
591
+ const joined = nonEmpty(input.reportId) ?? nonEmpty(env["PRISMA_BUILD_ID"]);
592
+ const buildId = joined !== void 0 ? joined : await api.create({
593
+ source: identity.source,
594
+ commitSha: identity.commitSha,
595
+ branchName: identity.branchName,
596
+ ...identity.runIdentity !== void 0 ? { runIdentity: identity.runIdentity } : {},
597
+ ...identity.externalLogUrl !== void 0 ? { externalLogUrl: identity.externalLogUrl } : {}
598
+ });
599
+ if (buildId === void 0) return void 0;
600
+ await api.update(buildId, {
601
+ phase: "deploy",
602
+ state: "running"
603
+ });
604
+ return session(api, buildId, options.refsOf, warn);
605
+ }
606
+ /**
607
+ * The app this run deployed and where it can be reached — but only when the
608
+ * run deployed exactly one compute service.
609
+ *
610
+ * `Build.appId` and `Build.deployedUrl` are each one value, and an app with
611
+ * several services has no single answer. Picking the first would put an
612
+ * arbitrary service's address in the Console and quietly imply it was the
613
+ * app's. Single-service apps are the common case and get a working link;
614
+ * multi-service apps get neither, and their services are all reported through
615
+ * the resources endpoint regardless.
616
+ *
617
+ * Both fields are fill-only, so this is safe to send on a build whose creator
618
+ * already set them to the same values, and a genuine disagreement is a 409
619
+ * the caller logs.
620
+ */
621
+ function deployedApp(entities) {
622
+ const services = entities.filter((entity) => entity.kind === "compute-service");
623
+ const only = services.length === 1 ? services[0] : void 0;
624
+ if (only === void 0) return {};
625
+ return {
626
+ appId: only.id,
627
+ ...only.url !== void 0 ? { deployedUrl: only.url } : {}
628
+ };
629
+ }
630
+ function session(api, buildId, refsOf, warn) {
631
+ let finished = false;
632
+ return {
633
+ childEnv: () => ({ [BUILD_ID_ENV]: buildId }),
634
+ /**
635
+ * Attaches the build to the Project and Branch this deploy resolved.
636
+ * Sent on its own rather than folded into the progress update above:
637
+ * the fields are fill-only, so keeping them in their own call means a
638
+ * 409 from a disagreeing creator costs these references alone.
639
+ */
640
+ async attach(input) {
641
+ if (input.container === void 0) return;
642
+ const { projectId, branchId } = refsOf(input.container);
643
+ await api.update(buildId, {
644
+ projectId,
645
+ ...branchId !== void 0 ? { branchId } : {}
646
+ });
647
+ },
648
+ async finish(outcome) {
649
+ if (finished) return;
650
+ finished = true;
651
+ try {
652
+ await api.update(buildId, {
653
+ state: outcome.ok ? "succeeded" : outcome.cancelled ? "cancelled" : "failed",
654
+ ...outcome.failingStep !== void 0 && !outcome.cancelled ? { failingStep: outcome.failingStep } : {},
655
+ ...outcome.errorMessage !== void 0 && !outcome.cancelled ? { errorMessage: outcome.errorMessage } : {},
656
+ ...deployedApp(outcome.entities)
657
+ });
658
+ } catch (error) {
659
+ warn(`Could not report this deploy's outcome: ${error instanceof Error ? error.message : String(error)}`);
660
+ }
661
+ }
662
+ };
663
+ }
664
+ //#endregion
257
665
  //#region ../../1-prisma-cloud/1-extensions/target/dist/control.mjs
258
666
  const PRISMA_NAME_MIN = 3;
259
667
  const PRISMA_NAME_MAX = 65;
@@ -701,6 +1109,15 @@ function s3StoreDescriptor(o) {
701
1109
  })
702
1110
  };
703
1111
  }
1112
+ function prismaCloudReporter() {
1113
+ return buildReporter({ refsOf: (container) => {
1114
+ const { projectId, branchId } = prismaCloudContainerOf(container);
1115
+ return {
1116
+ projectId,
1117
+ branchId
1118
+ };
1119
+ } });
1120
+ }
704
1121
  /** The Prisma Cloud–hosted deploy state store; its implementation lives in @internal/lowering. */
705
1122
  /**
706
1123
  * ADR-0031's registered provisioner for RPC_PEER_KEY: mints one `ServiceKey`
@@ -908,6 +1325,7 @@ const prismaCloud = (opts = {}) => {
908
1325
  container: containerDescriptor(),
909
1326
  providers: () => asProvidersLayer(Layer.mergeAll(providers(), PgWarmProvider(), PnMigrationProvider(), S3CredentialsProvider(), GeneratedParamProvider(), ServiceKeyProvider())),
910
1327
  preflight: (input) => runPreflight(input),
1328
+ reporter: prismaCloudReporter(),
911
1329
  application: { provision: (ctx) => Effect.gen(function* () {
912
1330
  const { projectId, branchId } = prismaCloudContainerOf(ctx.container);
913
1331
  for (const key of ["DATABASE_URL", "DATABASE_URL_POOLED"]) yield* EnvironmentVariable(`${key}-poison`, {