@kubb/cli 5.3.16 → 5.3.18

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 (64) hide show
  1. package/dist/{connect-IObjkkpy.js → connect-BP2OiMvo.js} +2 -2
  2. package/dist/{connect-IObjkkpy.js.map → connect-BP2OiMvo.js.map} +1 -1
  3. package/dist/{connect-CYlbWNUk.cjs → connect-CHiwWuCI.cjs} +2 -2
  4. package/dist/{connect-CYlbWNUk.cjs.map → connect-CHiwWuCI.cjs.map} +1 -1
  5. package/dist/{generate-RIE_QgE_.js → generate-C5NsehkJ.js} +2 -2
  6. package/dist/{generate-RIE_QgE_.js.map → generate-C5NsehkJ.js.map} +1 -1
  7. package/dist/{generate-By-otZHV.cjs → generate-CEMZP96A.cjs} +2 -2
  8. package/dist/{generate-By-otZHV.cjs.map → generate-CEMZP96A.cjs.map} +1 -1
  9. package/dist/index.cjs +7 -7
  10. package/dist/index.js +7 -7
  11. package/dist/{init-m87O7gWD.cjs → init-DMmtgniM.cjs} +2 -2
  12. package/dist/{init-m87O7gWD.cjs.map → init-DMmtgniM.cjs.map} +1 -1
  13. package/dist/{init-BTOW_efj.js → init-iw1ENf8f.js} +2 -2
  14. package/dist/{init-BTOW_efj.js.map → init-iw1ENf8f.js.map} +1 -1
  15. package/dist/{login-CA_xoFp7.js → login-AEDO32Tf.js} +2 -2
  16. package/dist/{login-CA_xoFp7.js.map → login-AEDO32Tf.js.map} +1 -1
  17. package/dist/{login-XpaX0zmW.cjs → login-CihXN93N.cjs} +2 -2
  18. package/dist/{login-XpaX0zmW.cjs.map → login-CihXN93N.cjs.map} +1 -1
  19. package/dist/{logout-BRZyHSgy.js → logout-Bw7zyqib.js} +2 -2
  20. package/dist/{logout-BRZyHSgy.js.map → logout-Bw7zyqib.js.map} +1 -1
  21. package/dist/{logout-CzsW6zdv.cjs → logout-rW7YPfoH.cjs} +2 -2
  22. package/dist/{logout-CzsW6zdv.cjs.map → logout-rW7YPfoH.cjs.map} +1 -1
  23. package/dist/{package-DvqvX4iy.cjs → package-CoU9wEFX.cjs} +2 -2
  24. package/dist/package-CoU9wEFX.cjs.map +1 -0
  25. package/dist/package-DV3bDcX_.js +6 -0
  26. package/dist/package-DV3bDcX_.js.map +1 -0
  27. package/dist/{run-BZr3INfK.js → run-4mmYto0l.js} +2 -2
  28. package/dist/{run-BZr3INfK.js.map → run-4mmYto0l.js.map} +1 -1
  29. package/dist/{run-DQGHRxvY.cjs → run-BT56dyNK.cjs} +2 -2
  30. package/dist/{run-DQGHRxvY.cjs.map → run-BT56dyNK.cjs.map} +1 -1
  31. package/dist/{run-BkpO4HyT.cjs → run-C-vNyjI_.cjs} +2 -2
  32. package/dist/{run-BkpO4HyT.cjs.map → run-C-vNyjI_.cjs.map} +1 -1
  33. package/dist/{run-DOOtoHGQ.js → run-D6QwS31H.js} +2 -2
  34. package/dist/run-D6QwS31H.js.map +1 -0
  35. package/dist/{run-DjMd8cvr.cjs → run-DZ-WUiiL.cjs} +2 -2
  36. package/dist/{run-DjMd8cvr.cjs.map → run-DZ-WUiiL.cjs.map} +1 -1
  37. package/dist/{run-DY-3ONFZ.cjs → run-Dp24WE9v.cjs} +2 -2
  38. package/dist/run-Dp24WE9v.cjs.map +1 -0
  39. package/dist/{run-jbHIvdOg.js → run-lQMyViFx.js} +2 -2
  40. package/dist/{run-jbHIvdOg.js.map → run-lQMyViFx.js.map} +1 -1
  41. package/dist/{run-Dy5NxCMm.js → run-v1KW3dcE.js} +2 -2
  42. package/dist/{run-Dy5NxCMm.js.map → run-v1KW3dcE.js.map} +1 -1
  43. package/dist/{snapshot-DJzr_LP8.cjs → snapshot-CixnEgJe.cjs} +71 -26
  44. package/dist/snapshot-CixnEgJe.cjs.map +1 -0
  45. package/dist/{snapshot-Cv2GHK1Y.js → snapshot-aiwOrbuI.js} +71 -26
  46. package/dist/snapshot-aiwOrbuI.js.map +1 -0
  47. package/dist/{status-BtXF2YAC.cjs → status-CYhqs_P9.cjs} +2 -2
  48. package/dist/{status-BtXF2YAC.cjs.map → status-CYhqs_P9.cjs.map} +1 -1
  49. package/dist/{status-DT1nKRUb.js → status-CkKR5TPs.js} +2 -2
  50. package/dist/{status-DT1nKRUb.js.map → status-CkKR5TPs.js.map} +1 -1
  51. package/dist/{studio-CwZBPZWN.js → studio-C2ex3VFM.js} +12 -7
  52. package/dist/studio-C2ex3VFM.js.map +1 -0
  53. package/dist/{studio-DDDkhrVO.cjs → studio-CVNxoOi0.cjs} +12 -7
  54. package/dist/studio-CVNxoOi0.cjs.map +1 -0
  55. package/package.json +7 -7
  56. package/dist/package-DjDsh9cp.js +0 -6
  57. package/dist/package-DjDsh9cp.js.map +0 -1
  58. package/dist/package-DvqvX4iy.cjs.map +0 -1
  59. package/dist/run-DOOtoHGQ.js.map +0 -1
  60. package/dist/run-DY-3ONFZ.cjs.map +0 -1
  61. package/dist/snapshot-Cv2GHK1Y.js.map +0 -1
  62. package/dist/snapshot-DJzr_LP8.cjs.map +0 -1
  63. package/dist/studio-CwZBPZWN.js.map +0 -1
  64. package/dist/studio-DDDkhrVO.cjs.map +0 -1
@@ -1,8 +1,9 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
2
- const require_run = require("./run-DY-3ONFZ.cjs");
2
+ const require_run = require("./run-Dp24WE9v.cjs");
3
3
  const require_utils = require("./utils-DqUNntyL.cjs");
4
4
  const require_output = require("./output-D-9bGS7y.cjs");
5
5
  let node_util = require("node:util");
6
+ let node_crypto = require("node:crypto");
6
7
  let node_process = require("node:process");
7
8
  node_process = require_rolldown_runtime.__toESM(node_process, 1);
8
9
  let node_path = require("node:path");
@@ -10,15 +11,19 @@ let node_fs = require("node:fs");
10
11
  let _kubb_core = require("@kubb/core");
11
12
  let _kubb_studio = require("@kubb/studio");
12
13
  //#region src/runners/studio/ci.ts
13
- function readGithubPullRequestNumber(eventPath) {
14
+ function readGithubPullRequest(eventPath) {
14
15
  if (!eventPath) return;
15
16
  try {
16
17
  const event = JSON.parse((0, node_fs.readFileSync)(eventPath, "utf8"));
17
- return event.pull_request?.number ? String(event.pull_request.number) : void 0;
18
+ return event.pull_request?.number ? event.pull_request : void 0;
18
19
  } catch {
19
20
  return;
20
21
  }
21
22
  }
23
+ /** Reproduces GitLab's `CI_COMMIT_REF_SLUG` for a branch it gives no slug of, like a merge request's target. */
24
+ function gitlabRefSlug(ref) {
25
+ return ref.toLowerCase().replace(/[^a-z0-9]/gu, "-").slice(0, 63).replace(/^-+|-+$/g, "");
26
+ }
22
27
  /**
23
28
  * Detects the CI provider from its environment variables and derives a stable identity from it.
24
29
  * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has
@@ -26,21 +31,32 @@ function readGithubPullRequestNumber(eventPath) {
26
31
  */
27
32
  function detectCi(env = node_process.default.env) {
28
33
  if (env.GITHUB_ACTIONS) {
29
- const pullRequestNumber = readGithubPullRequestNumber(env.GITHUB_EVENT_PATH);
34
+ const pullRequest = readGithubPullRequest(env.GITHUB_EVENT_PATH);
30
35
  const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? "";
31
- const scope = pullRequestNumber ?? env.GITHUB_RUN_ID ?? "";
36
+ const branch = env.GITHUB_REF?.startsWith("refs/heads/") ? env.GITHUB_REF.slice(11) : void 0;
37
+ const scope = pullRequest?.number ?? (branch ? `refs/heads/${branch}` : env.GITHUB_RUN_ID) ?? "";
38
+ const baseBranch = pullRequest?.base?.ref;
32
39
  return {
33
40
  id: `gh:${repositoryId}:${scope}`,
34
- name: `${env.GITHUB_REPOSITORY ?? "github"}#${scope}`,
35
- commit: env.GITHUB_SHA
41
+ name: `${env.GITHUB_REPOSITORY ?? "github"}#${pullRequest?.number ?? branch ?? scope}`,
42
+ commit: pullRequest?.head?.sha ?? env.GITHUB_SHA,
43
+ base: baseBranch ? {
44
+ branch: baseBranch,
45
+ id: `gh:${repositoryId}:refs/heads/${baseBranch}`
46
+ } : void 0
36
47
  };
37
48
  }
38
49
  if (env.GITLAB_CI) {
39
50
  const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? "";
51
+ const baseBranch = env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
40
52
  return {
41
53
  id: `gl:${env.CI_PROJECT_ID ?? ""}:${scope}`,
42
54
  name: `${env.CI_PROJECT_PATH ?? "gitlab"}#${scope}`,
43
- commit: env.CI_COMMIT_SHA
55
+ commit: env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || env.CI_COMMIT_SHA,
56
+ base: baseBranch ? {
57
+ branch: baseBranch,
58
+ id: `gl:${env.CI_PROJECT_ID ?? ""}:${gitlabRefSlug(baseBranch)}`
59
+ } : void 0
44
60
  };
45
61
  }
46
62
  if (env.BITBUCKET_BUILD_NUMBER) {
@@ -115,13 +131,20 @@ function resolveToken(options) {
115
131
  }
116
132
  function resolveCiIdentity(options) {
117
133
  const detected = detectCi();
118
- if (options.id) return {
134
+ const ci = options.id ? {
119
135
  id: options.id,
120
136
  name: detected?.name ?? options.id,
121
137
  commit: detected?.commit
138
+ } : detected;
139
+ if (!ci) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
140
+ const base = options.baseId ? {
141
+ branch: options.baseId,
142
+ id: options.baseId
143
+ } : ci.base;
144
+ return {
145
+ ...ci,
146
+ base: base?.id === ci.id ? void 0 : base
122
147
  };
123
- if (!detected) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
124
- return detected;
125
148
  }
126
149
  /** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */
127
150
  function resolveTimeoutMs(options) {
@@ -137,7 +160,7 @@ function assertSecureStudioUrl(studioUrl) {
137
160
  function absoluteUrl(studioUrl, path) {
138
161
  return new URL(path, `${studioUrl}/`).toString();
139
162
  }
140
- function toResult(studioUrl, snapshot, agentSlug) {
163
+ function toResult(studioUrl, snapshot, agentSlug, ci) {
141
164
  return {
142
165
  id: snapshot.id,
143
166
  name: snapshot.name,
@@ -147,25 +170,33 @@ function toResult(studioUrl, snapshot, agentSlug) {
147
170
  snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),
148
171
  expiresAt: snapshot.expiresAt,
149
172
  agentUrl: absoluteUrl(studioUrl, `/agents/${agentSlug}`),
150
- changes: snapshot.changes
173
+ changes: snapshot.changes,
174
+ branchChanges: snapshot.branchChanges && ci.base ? {
175
+ ...snapshot.branchChanges,
176
+ branch: ci.base.branch
177
+ } : void 0
151
178
  };
152
179
  }
180
+ /** How many files differ, `against` what. */
181
+ function describeChanges(changes, against) {
182
+ const { added, changed, removed } = changes;
183
+ return added.length + changed.length + removed.length ? `${added.length} added, ${changed.length} changed, ${removed.length} removed ${against}` : `No changes ${against}`;
184
+ }
153
185
  /** One summary line: how many files changed, and since which run. */
154
186
  function formatChanges(changes) {
155
187
  if (!changes.base) return "First snapshot";
156
- const counts = [
157
- `${changes.added.length} added`,
158
- `${changes.changed.length} changed`,
159
- `${changes.removed.length} removed`
160
- ].join(", ");
161
- const since = changes.base.commit ? changes.base.commit.slice(0, 7) : changes.base.createdAt;
162
- return changes.added.length + changes.changed.length + changes.removed.length ? `${counts} since ${since}` : `No changes since ${since}`;
188
+ return describeChanges(changes, `since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}`);
189
+ }
190
+ /** One summary line: how the snapshot differs from the base branch's latest one. */
191
+ function formatBranchChanges(changes) {
192
+ return changes.base ? describeChanges(changes, `against ${changes.branch}`) : `No snapshot of ${changes.branch} to compare with`;
163
193
  }
164
194
  function printSummary(result) {
165
195
  require_output.logBlock([
166
196
  `${(0, node_util.styleText)("dim", "Package".padEnd(10))} ${result.name ?? "(unnamed)"}@${result.version ?? "0.0.0"}`,
167
197
  `${(0, node_util.styleText)("dim", "Tarball".padEnd(10))} ${(0, node_util.styleText)("cyan", result.url)}`,
168
198
  `${(0, node_util.styleText)("dim", "Agent".padEnd(10))} ${result.agentUrl}`,
199
+ ...result.branchChanges ? [`${(0, node_util.styleText)("dim", "Branch".padEnd(10))} ${formatBranchChanges(result.branchChanges)}`] : [],
169
200
  ...result.changes ? [`${(0, node_util.styleText)("dim", "Changes".padEnd(10))} ${formatChanges(result.changes)}`] : [],
170
201
  `${(0, node_util.styleText)("dim", "Expires".padEnd(10))} ${result.expiresAt}`
171
202
  ]);
@@ -196,13 +227,15 @@ async function snapshot(options) {
196
227
  name: ci.name,
197
228
  machineToken: (0, _kubb_studio.machineTokenFrom)(ci.id)
198
229
  });
230
+ const instanceId = (0, node_crypto.randomUUID)();
199
231
  const { promise: ready, resolve: markReady } = Promise.withResolvers();
200
232
  const shutdown = new AbortController();
201
- const lost = (0, _kubb_studio.runConnection)({
233
+ const connection = (0, _kubb_studio.runConnection)({
202
234
  credentials: { token: agent.token },
203
235
  signal: shutdown.signal,
204
236
  clientOptions: () => ({
205
237
  studioUrl: options.studioUrl,
238
+ instanceId,
206
239
  configPath,
207
240
  root: node_process.default.cwd(),
208
241
  version: options.version,
@@ -218,7 +251,11 @@ async function snapshot(options) {
218
251
  }
219
252
  }),
220
253
  onTokenRejected: ({ error }) => Promise.reject(error)
221
- }).then(() => Promise.reject(/* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished")));
254
+ });
255
+ const connectionLost = new AbortController();
256
+ const connectionEnded = /* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished");
257
+ connection.then(() => connectionLost.abort(connectionEnded), (error) => connectionLost.abort(error));
258
+ const lost = connection.then(() => Promise.reject(connectionEnded), (error) => Promise.reject(error));
222
259
  lost.catch(() => {});
223
260
  try {
224
261
  let readyTimeout;
@@ -234,25 +271,32 @@ async function snapshot(options) {
234
271
  clearTimeout(readyTimeout);
235
272
  }
236
273
  step("Creating snapshot job");
274
+ const snapshotDeadline = Date.now() + timeoutMs;
275
+ const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1);
237
276
  const job = await (0, _kubb_studio.createJob)({
238
277
  studioUrl: options.studioUrl,
239
278
  token,
240
279
  type: "snapshot",
241
280
  agentId: agent.id,
281
+ instanceId,
242
282
  name,
243
283
  version: packageVersion,
244
- commit: ci.commit
284
+ commit: ci.commit,
285
+ baseId: ci.base?.id,
286
+ timeoutMs: snapshotTimeoutMs(),
287
+ signal: connectionLost.signal
245
288
  });
246
289
  step(`Snapshot job queued: ${job.id}`);
247
290
  const finished = await Promise.race([(0, _kubb_studio.waitForJob)({
248
291
  studioUrl: options.studioUrl,
249
292
  token,
250
293
  id: job.id,
251
- timeoutMs
294
+ timeoutMs: snapshotTimeoutMs(),
295
+ signal: connectionLost.signal
252
296
  }), lost]);
253
297
  if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
254
298
  if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
255
- const result = toResult(options.studioUrl, finished.snapshot, agent.slug);
299
+ const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci);
256
300
  step("Snapshot published");
257
301
  if (options.json) console.log(JSON.stringify(result));
258
302
  if (!options.json) printSummary(result);
@@ -269,6 +313,7 @@ const runner = async ({ values }) => {
269
313
  ...require_run.createStudioOptions(values),
270
314
  token: values.token,
271
315
  id: values.id,
316
+ baseId: values.baseId,
272
317
  name: values.name,
273
318
  packageVersion: values.packageVersion,
274
319
  timeout: values.timeout,
@@ -279,4 +324,4 @@ const runner = async ({ values }) => {
279
324
  //#endregion
280
325
  exports.runner = runner;
281
326
 
282
- //# sourceMappingURL=snapshot-DJzr_LP8.cjs.map
327
+ //# sourceMappingURL=snapshot-CixnEgJe.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-CixnEgJe.cjs","names":["readFileSync","process","join","exists","read","dirname","process","styleText","loadConfigs","logLevelMap","createPlainLogger","createAgent","machineTokenFrom","randomUUID","runConnection","setupReporters","cliReporter","createJob","waitForJob","createStudioOptions","run"],"sources":["../src/runners/studio/ci.ts","../src/runners/studio/snapshot.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport process from 'node:process'\n\n/**\n * Stable identity for the CI agent a snapshot run registers or reuses.\n */\nexport type CiContext = {\n /**\n * Fed into the machine token, so the same merge or pull request reuses one agent.\n */\n id: string\n /**\n * Agent display name shown in Studio.\n */\n name: string\n /** The commit this run builds, so the next snapshot can diff against it. */\n commit?: string\n /** The branch a pull request merges into, and the `id` runs on that branch register under. */\n base?: { branch: string; id: string }\n}\n\ntype GithubPullRequest = { number: number; head?: { sha?: string }; base?: { ref?: string } }\n\nfunction readGithubPullRequest(eventPath: string | undefined): GithubPullRequest | undefined {\n if (!eventPath) {\n return undefined\n }\n\n try {\n const event = JSON.parse(readFileSync(eventPath, 'utf8')) as { pull_request?: GithubPullRequest }\n return event.pull_request?.number ? event.pull_request : undefined\n } catch {\n return undefined\n }\n}\n\n/** Reproduces GitLab's `CI_COMMIT_REF_SLUG` for a branch it gives no slug of, like a merge request's target. */\nexport function gitlabRefSlug(ref: string): string {\n return ref\n .toLowerCase()\n .replace(/[^a-z0-9]/gu, '-')\n .slice(0, 63)\n .replace(/^-+|-+$/g, '')\n}\n\n/**\n * Detects the CI provider from its environment variables and derives a stable identity from it.\n * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has\n * always registered agents under, so an existing repository keeps reusing its agent.\n */\nexport function detectCi(env: Record<string, string | undefined> = process.env): CiContext | null {\n if (env.GITHUB_ACTIONS) {\n const pullRequest = readGithubPullRequest(env.GITHUB_EVENT_PATH)\n const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? ''\n const branch = env.GITHUB_REF?.startsWith('refs/heads/') ? env.GITHUB_REF.slice('refs/heads/'.length) : undefined\n // A branch run is scoped by its ref, which a pull request number never collides with.\n const scope = pullRequest?.number ?? (branch ? `refs/heads/${branch}` : env.GITHUB_RUN_ID) ?? ''\n const baseBranch = pullRequest?.base?.ref\n\n return {\n id: `gh:${repositoryId}:${scope}`,\n name: `${env.GITHUB_REPOSITORY ?? 'github'}#${pullRequest?.number ?? branch ?? scope}`,\n // The head commit, not the merge commit a pull request run checks out.\n commit: pullRequest?.head?.sha ?? env.GITHUB_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gh:${repositoryId}:refs/heads/${baseBranch}` } : undefined,\n }\n }\n\n if (env.GITLAB_CI) {\n const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? ''\n const baseBranch = env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME\n\n return {\n id: `gl:${env.CI_PROJECT_ID ?? ''}:${scope}`,\n name: `${env.CI_PROJECT_PATH ?? 'gitlab'}#${scope}`,\n commit: env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || env.CI_COMMIT_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gl:${env.CI_PROJECT_ID ?? ''}:${gitlabRefSlug(baseBranch)}` } : undefined,\n }\n }\n\n if (env.BITBUCKET_BUILD_NUMBER) {\n const scope = env.BITBUCKET_PR_ID ?? env.BITBUCKET_BRANCH ?? ''\n\n return { id: `bb:${env.BITBUCKET_REPO_UUID ?? ''}:${scope}`, name: `${env.BITBUCKET_REPO_FULL_NAME ?? 'bitbucket'}#${scope}`, commit: env.BITBUCKET_COMMIT }\n }\n\n if (env.CIRCLECI) {\n // Owner-qualified: two projects with the same name under different CircleCI orgs must not\n // collide on one agent, since registering it purges the other project's live sessions.\n const owner = env.CIRCLE_PROJECT_USERNAME ?? ''\n const project = env.CIRCLE_PROJECT_REPONAME ?? 'circleci'\n const scope = env.CIRCLE_PR_NUMBER ?? env.CIRCLE_BRANCH ?? ''\n\n return { id: `circle:${owner}:${project}:${scope}`, name: `${owner ? `${owner}/` : ''}${project}#${scope}`, commit: env.CIRCLE_SHA1 }\n }\n\n return null\n}\n","import { randomUUID } from 'node:crypto'\nimport { dirname, join } from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { exists, read } from '@internals/utils'\nimport { cliReporter, logLevel as logLevelMap } from '@kubb/core'\nimport { createAgent, createJob, machineTokenFrom, runConnection, waitForJob, type StudioSnapshot, type StudioSnapshotChanges } from '@kubb/studio'\nimport { logBlock } from '../../loggers/output.ts'\nimport { createPlainLogger } from '../../loggers/plainLogger.ts'\nimport setupReporters from '../../loggers/utils.ts'\nimport { type CiContext, detectCi } from './ci.ts'\nimport { createStudioOptions, loadConfigs, run, type SnapshotOptions } from './run.ts'\nimport type { definition } from '../../commands/studio/snapshot.ts'\nimport type { CommandRunner } from 'gunshi'\n\n/**\n * How long to wait for Studio's `studio:ready` acknowledgement, above the client's own 10s\n * handshake-ack timeout.\n */\nconst READY_TIMEOUT_MS = 15_000\n\n/**\n * Longest `--timeout` accepted. Job records expire from Studio's storage after an hour.\n */\nconst MAX_TIMEOUT_SECONDS = 3600\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1'])\n\nasync function findPackageJson(startDirectory: string): Promise<{ name: string; version: string }> {\n let directory = startDirectory\n\n for (;;) {\n const file = join(directory, 'package.json')\n\n if (await exists(file)) {\n const packageJson = JSON.parse(await read(file)) as Partial<{ name: string; version: string }>\n\n if (packageJson.name && packageJson.version) {\n return { name: packageJson.name, version: packageJson.version }\n }\n }\n\n const parent = dirname(directory)\n\n if (parent === directory) {\n break\n }\n\n directory = parent\n }\n\n throw new Error(`No package.json with a name and a version found above ${startDirectory}`)\n}\n\n/** Reads package.json only for whichever of name/version --name and --version did not supply. */\nasync function resolvePackageMetadata(options: SnapshotOptions): Promise<{ name: string; version: string }> {\n if (options.name && options.packageVersion) {\n return { name: options.name, version: options.packageVersion }\n }\n\n const packageMetadata = await findPackageJson(process.cwd())\n\n return { name: options.name ?? packageMetadata.name, version: options.packageVersion ?? packageMetadata.version }\n}\n\nfunction resolveToken(options: SnapshotOptions): string {\n const token = options.token ?? process.env.KUBB_TOKEN\n\n if (!token) {\n throw new Error('An organization CI API key is required. Pass --token or set KUBB_TOKEN.')\n }\n\n return token\n}\n\nfunction resolveCiIdentity(options: SnapshotOptions): CiContext {\n const detected = detectCi()\n // A custom --id drops the detected base, since runs on that branch may use a custom id too.\n const ci = options.id ? { id: options.id, name: detected?.name ?? options.id, commit: detected?.commit } : detected\n\n if (!ci) {\n throw new Error('Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.')\n }\n\n // An explicit --base-id may not name a branch, so it labels the comparison itself.\n const base = options.baseId ? { branch: options.baseId, id: options.baseId } : ci.base\n\n return { ...ci, base: base?.id === ci.id ? undefined : base }\n}\n\n/** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */\nfunction resolveTimeoutMs(options: SnapshotOptions): number {\n const seconds = options.timeout ?? 600\n\n if (!Number.isFinite(seconds) || seconds <= 0) {\n throw new Error('--timeout must be a positive number of seconds')\n }\n\n return Math.min(seconds, MAX_TIMEOUT_SECONDS) * 1000\n}\n\n/** The CI key is a credential, so refuse to send it anywhere but HTTPS or a loopback dev host. */\nfunction assertSecureStudioUrl(studioUrl: string): void {\n const url = new URL(studioUrl)\n\n if (url.protocol !== 'https:' && !LOOPBACK_HOSTS.has(url.hostname)) {\n throw new Error(`Refusing to send the CI API key to ${studioUrl}. Use an https:// Studio URL, or a loopback host for local development.`)\n }\n}\n\nfunction absoluteUrl(studioUrl: string, path: string): string {\n return new URL(path, `${studioUrl}/`).toString()\n}\n\ntype SnapshotResult = {\n id: string\n name: string | null\n version: string | null\n integrity: string | null\n url: string\n snapshotIdUrl: string\n expiresAt: string\n agentUrl: string\n /** What changed since the previous snapshot of this package on this agent. */\n changes?: StudioSnapshotChanges\n /** What differs from the latest snapshot of the base branch, named by `branch`. */\n branchChanges?: StudioSnapshotChanges & { branch: string }\n}\n\nfunction toResult(studioUrl: string, snapshot: StudioSnapshot, agentSlug: string, ci: CiContext): SnapshotResult {\n return {\n id: snapshot.id,\n name: snapshot.name,\n version: snapshot.version,\n integrity: snapshot.integrity,\n url: absoluteUrl(studioUrl, snapshot.url),\n snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),\n expiresAt: snapshot.expiresAt,\n agentUrl: absoluteUrl(studioUrl, `/agents/${agentSlug}`),\n changes: snapshot.changes,\n branchChanges: snapshot.branchChanges && ci.base ? { ...snapshot.branchChanges, branch: ci.base.branch } : undefined,\n }\n}\n\n/** How many files differ, `against` what. */\nfunction describeChanges(changes: StudioSnapshotChanges, against: string): string {\n const { added, changed, removed } = changes\n\n return added.length + changed.length + removed.length\n ? `${added.length} added, ${changed.length} changed, ${removed.length} removed ${against}`\n : `No changes ${against}`\n}\n\n/** One summary line: how many files changed, and since which run. */\nexport function formatChanges(changes: StudioSnapshotChanges): string {\n if (!changes.base) {\n return 'First snapshot'\n }\n\n return describeChanges(changes, `since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}`)\n}\n\n/** One summary line: how the snapshot differs from the base branch's latest one. */\nexport function formatBranchChanges(changes: StudioSnapshotChanges & { branch: string }): string {\n return changes.base ? describeChanges(changes, `against ${changes.branch}`) : `No snapshot of ${changes.branch} to compare with`\n}\n\nfunction printSummary(result: SnapshotResult): void {\n logBlock([\n `${styleText('dim', 'Package'.padEnd(10))} ${result.name ?? '(unnamed)'}@${result.version ?? '0.0.0'}`,\n `${styleText('dim', 'Tarball'.padEnd(10))} ${styleText('cyan', result.url)}`,\n `${styleText('dim', 'Agent'.padEnd(10))} ${result.agentUrl}`,\n ...(result.branchChanges ? [`${styleText('dim', 'Branch'.padEnd(10))} ${formatBranchChanges(result.branchChanges)}`] : []),\n ...(result.changes ? [`${styleText('dim', 'Changes'.padEnd(10))} ${formatChanges(result.changes)}`] : []),\n `${styleText('dim', 'Expires'.padEnd(10))} ${result.expiresAt}`,\n ])\n}\n\n/**\n * Generates a Kubb Studio snapshot from a script: registers or reuses a CI agent, connects it,\n * queues a snapshot job, and polls until the tarball is ready. A snapshot job runs generation and\n * packs the tarball in one step, so this needs no separate generation run.\n */\nexport async function snapshot(options: SnapshotOptions): Promise<void> {\n const timeoutMs = resolveTimeoutMs(options)\n const token = resolveToken(options)\n assertSecureStudioUrl(options.studioUrl)\n const { configPath } = await loadConfigs(options)\n const ci = resolveCiIdentity(options)\n const { name, version: packageVersion } = await resolvePackageMetadata(options)\n const logLevel = logLevelMap[options.logLevel ?? 'info']\n\n // Set before the first `getMachineToken()` call (inside the connection), so the WebSocket\n // session registers under the same machine token `createAgent` just registered with Studio.\n process.env.KUBB_AGENT_SECRET = ci.id\n\n const write = options.json ? (line: string) => console.error(line) : (line: string) => console.log(line)\n const logger = createPlainLogger(write)\n const step = (message: string) => {\n if (logLevel > logLevelMap.silent) {\n write(message)\n }\n }\n\n step('Creating Kubb Studio agent')\n\n const agent = await createAgent({ studioUrl: options.studioUrl, token, name: ci.name, machineToken: machineTokenFrom(ci.id) })\n // Overlapping runs of one pipeline connect under the same CI agent. Naming this process lets the\n // snapshot job run here, on this run's checkout, and not on another run's.\n const instanceId = randomUUID()\n\n const { promise: ready, resolve: markReady } = Promise.withResolvers<void>()\n const shutdown = new AbortController()\n\n const connection = runConnection({\n credentials: { token: agent.token },\n signal: shutdown.signal,\n clientOptions: () => ({\n studioUrl: options.studioUrl,\n instanceId,\n configPath,\n root: process.cwd(),\n version: options.version,\n // Flags only: a script is never prompted and never reuses a saved answer.\n permissions: options.permission,\n loadConfig: async () => (await loadConfigs(options)).config,\n installLogger: async (hooks) => {\n await setupReporters(hooks, { logLevel, reporters: [cliReporter], logger })\n hooks.hook('studio:ready', () => markReady())\n },\n }),\n // A CI agent's token comes from the organization key, so there is no pairing to fall back to.\n onTokenRejected: ({ error }) => Promise.reject(error),\n })\n const connectionLost = new AbortController()\n const connectionEnded = new Error('The Kubb Studio connection ended before the snapshot finished')\n void connection.then(\n () => connectionLost.abort(connectionEnded),\n (error) => connectionLost.abort(error),\n )\n const lost = connection.then(\n () => Promise.reject(connectionEnded),\n (error) => Promise.reject(error),\n )\n void lost.catch(() => {})\n\n try {\n let readyTimeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n ready,\n lost,\n new Promise<never>((_, reject) => {\n readyTimeout = setTimeout(() => reject(new Error('Timed out waiting for Kubb Studio to confirm the agent was ready')), READY_TIMEOUT_MS)\n }),\n ])\n } finally {\n clearTimeout(readyTimeout)\n }\n\n step('Creating snapshot job')\n\n const snapshotDeadline = Date.now() + timeoutMs\n const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1)\n\n const job = await createJob({\n studioUrl: options.studioUrl,\n token,\n type: 'snapshot',\n agentId: agent.id,\n instanceId,\n name,\n version: packageVersion,\n commit: ci.commit,\n baseId: ci.base?.id,\n timeoutMs: snapshotTimeoutMs(),\n signal: connectionLost.signal,\n })\n step(`Snapshot job queued: ${job.id}`)\n const finished = await Promise.race([\n waitForJob({\n studioUrl: options.studioUrl,\n token,\n id: job.id,\n timeoutMs: snapshotTimeoutMs(),\n signal: connectionLost.signal,\n }),\n lost,\n ])\n\n if (finished.status === 'failed') {\n throw new Error(finished.error ?? 'Snapshot job failed')\n }\n\n if (!finished.snapshot) {\n throw new Error('Snapshot job succeeded without a snapshot')\n }\n\n const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci)\n\n step('Snapshot published')\n\n if (options.json) {\n console.log(JSON.stringify(result))\n }\n if (!options.json) {\n printSummary(result)\n }\n } catch (error) {\n step('Snapshot failed')\n throw error\n } finally {\n step('Disconnecting from Kubb Studio')\n // Not awaited: a half-open socket must not hold the run open.\n shutdown.abort()\n }\n}\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options: SnapshotOptions = {\n ...createStudioOptions(values),\n token: values.token,\n id: values.id,\n baseId: values.baseId,\n name: values.name,\n packageVersion: values.packageVersion,\n timeout: values.timeout,\n json: values.json,\n }\n await run(options, () => snapshot(options), { json: options.json })\n}\n"],"mappings":"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"}
@@ -1,23 +1,28 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
- import { a as run, c as exists, l as read, n as createStudioOptions, r as loadConfigs } from "./run-DOOtoHGQ.js";
2
+ import { a as run, c as exists, l as read, n as createStudioOptions, r as loadConfigs } from "./run-D6QwS31H.js";
3
3
  import { d as createPlainLogger, u as setupReporters } from "./utils-TpILNAy7.js";
4
4
  import { r as logBlock } from "./output-C_Gk4p-8.js";
5
5
  import { styleText } from "node:util";
6
+ import { randomUUID } from "node:crypto";
6
7
  import process from "node:process";
7
8
  import { dirname, join } from "node:path";
8
9
  import { readFileSync } from "node:fs";
9
10
  import { cliReporter, logLevel } from "@kubb/core";
10
11
  import { createAgent, createJob, machineTokenFrom, runConnection, waitForJob } from "@kubb/studio";
11
12
  //#region src/runners/studio/ci.ts
12
- function readGithubPullRequestNumber(eventPath) {
13
+ function readGithubPullRequest(eventPath) {
13
14
  if (!eventPath) return;
14
15
  try {
15
16
  const event = JSON.parse(readFileSync(eventPath, "utf8"));
16
- return event.pull_request?.number ? String(event.pull_request.number) : void 0;
17
+ return event.pull_request?.number ? event.pull_request : void 0;
17
18
  } catch {
18
19
  return;
19
20
  }
20
21
  }
22
+ /** Reproduces GitLab's `CI_COMMIT_REF_SLUG` for a branch it gives no slug of, like a merge request's target. */
23
+ function gitlabRefSlug(ref) {
24
+ return ref.toLowerCase().replace(/[^a-z0-9]/gu, "-").slice(0, 63).replace(/^-+|-+$/g, "");
25
+ }
21
26
  /**
22
27
  * Detects the CI provider from its environment variables and derives a stable identity from it.
23
28
  * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has
@@ -25,21 +30,32 @@ function readGithubPullRequestNumber(eventPath) {
25
30
  */
26
31
  function detectCi(env = process.env) {
27
32
  if (env.GITHUB_ACTIONS) {
28
- const pullRequestNumber = readGithubPullRequestNumber(env.GITHUB_EVENT_PATH);
33
+ const pullRequest = readGithubPullRequest(env.GITHUB_EVENT_PATH);
29
34
  const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? "";
30
- const scope = pullRequestNumber ?? env.GITHUB_RUN_ID ?? "";
35
+ const branch = env.GITHUB_REF?.startsWith("refs/heads/") ? env.GITHUB_REF.slice(11) : void 0;
36
+ const scope = pullRequest?.number ?? (branch ? `refs/heads/${branch}` : env.GITHUB_RUN_ID) ?? "";
37
+ const baseBranch = pullRequest?.base?.ref;
31
38
  return {
32
39
  id: `gh:${repositoryId}:${scope}`,
33
- name: `${env.GITHUB_REPOSITORY ?? "github"}#${scope}`,
34
- commit: env.GITHUB_SHA
40
+ name: `${env.GITHUB_REPOSITORY ?? "github"}#${pullRequest?.number ?? branch ?? scope}`,
41
+ commit: pullRequest?.head?.sha ?? env.GITHUB_SHA,
42
+ base: baseBranch ? {
43
+ branch: baseBranch,
44
+ id: `gh:${repositoryId}:refs/heads/${baseBranch}`
45
+ } : void 0
35
46
  };
36
47
  }
37
48
  if (env.GITLAB_CI) {
38
49
  const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? "";
50
+ const baseBranch = env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
39
51
  return {
40
52
  id: `gl:${env.CI_PROJECT_ID ?? ""}:${scope}`,
41
53
  name: `${env.CI_PROJECT_PATH ?? "gitlab"}#${scope}`,
42
- commit: env.CI_COMMIT_SHA
54
+ commit: env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || env.CI_COMMIT_SHA,
55
+ base: baseBranch ? {
56
+ branch: baseBranch,
57
+ id: `gl:${env.CI_PROJECT_ID ?? ""}:${gitlabRefSlug(baseBranch)}`
58
+ } : void 0
43
59
  };
44
60
  }
45
61
  if (env.BITBUCKET_BUILD_NUMBER) {
@@ -114,13 +130,20 @@ function resolveToken(options) {
114
130
  }
115
131
  function resolveCiIdentity(options) {
116
132
  const detected = detectCi();
117
- if (options.id) return {
133
+ const ci = options.id ? {
118
134
  id: options.id,
119
135
  name: detected?.name ?? options.id,
120
136
  commit: detected?.commit
137
+ } : detected;
138
+ if (!ci) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
139
+ const base = options.baseId ? {
140
+ branch: options.baseId,
141
+ id: options.baseId
142
+ } : ci.base;
143
+ return {
144
+ ...ci,
145
+ base: base?.id === ci.id ? void 0 : base
121
146
  };
122
- if (!detected) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
123
- return detected;
124
147
  }
125
148
  /** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */
126
149
  function resolveTimeoutMs(options) {
@@ -136,7 +159,7 @@ function assertSecureStudioUrl(studioUrl) {
136
159
  function absoluteUrl(studioUrl, path) {
137
160
  return new URL(path, `${studioUrl}/`).toString();
138
161
  }
139
- function toResult(studioUrl, snapshot, agentSlug) {
162
+ function toResult(studioUrl, snapshot, agentSlug, ci) {
140
163
  return {
141
164
  id: snapshot.id,
142
165
  name: snapshot.name,
@@ -146,25 +169,33 @@ function toResult(studioUrl, snapshot, agentSlug) {
146
169
  snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),
147
170
  expiresAt: snapshot.expiresAt,
148
171
  agentUrl: absoluteUrl(studioUrl, `/agents/${agentSlug}`),
149
- changes: snapshot.changes
172
+ changes: snapshot.changes,
173
+ branchChanges: snapshot.branchChanges && ci.base ? {
174
+ ...snapshot.branchChanges,
175
+ branch: ci.base.branch
176
+ } : void 0
150
177
  };
151
178
  }
179
+ /** How many files differ, `against` what. */
180
+ function describeChanges(changes, against) {
181
+ const { added, changed, removed } = changes;
182
+ return added.length + changed.length + removed.length ? `${added.length} added, ${changed.length} changed, ${removed.length} removed ${against}` : `No changes ${against}`;
183
+ }
152
184
  /** One summary line: how many files changed, and since which run. */
153
185
  function formatChanges(changes) {
154
186
  if (!changes.base) return "First snapshot";
155
- const counts = [
156
- `${changes.added.length} added`,
157
- `${changes.changed.length} changed`,
158
- `${changes.removed.length} removed`
159
- ].join(", ");
160
- const since = changes.base.commit ? changes.base.commit.slice(0, 7) : changes.base.createdAt;
161
- return changes.added.length + changes.changed.length + changes.removed.length ? `${counts} since ${since}` : `No changes since ${since}`;
187
+ return describeChanges(changes, `since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}`);
188
+ }
189
+ /** One summary line: how the snapshot differs from the base branch's latest one. */
190
+ function formatBranchChanges(changes) {
191
+ return changes.base ? describeChanges(changes, `against ${changes.branch}`) : `No snapshot of ${changes.branch} to compare with`;
162
192
  }
163
193
  function printSummary(result) {
164
194
  logBlock([
165
195
  `${styleText("dim", "Package".padEnd(10))} ${result.name ?? "(unnamed)"}@${result.version ?? "0.0.0"}`,
166
196
  `${styleText("dim", "Tarball".padEnd(10))} ${styleText("cyan", result.url)}`,
167
197
  `${styleText("dim", "Agent".padEnd(10))} ${result.agentUrl}`,
198
+ ...result.branchChanges ? [`${styleText("dim", "Branch".padEnd(10))} ${formatBranchChanges(result.branchChanges)}`] : [],
168
199
  ...result.changes ? [`${styleText("dim", "Changes".padEnd(10))} ${formatChanges(result.changes)}`] : [],
169
200
  `${styleText("dim", "Expires".padEnd(10))} ${result.expiresAt}`
170
201
  ]);
@@ -195,13 +226,15 @@ async function snapshot(options) {
195
226
  name: ci.name,
196
227
  machineToken: machineTokenFrom(ci.id)
197
228
  });
229
+ const instanceId = randomUUID();
198
230
  const { promise: ready, resolve: markReady } = Promise.withResolvers();
199
231
  const shutdown = new AbortController();
200
- const lost = runConnection({
232
+ const connection = runConnection({
201
233
  credentials: { token: agent.token },
202
234
  signal: shutdown.signal,
203
235
  clientOptions: () => ({
204
236
  studioUrl: options.studioUrl,
237
+ instanceId,
205
238
  configPath,
206
239
  root: process.cwd(),
207
240
  version: options.version,
@@ -217,7 +250,11 @@ async function snapshot(options) {
217
250
  }
218
251
  }),
219
252
  onTokenRejected: ({ error }) => Promise.reject(error)
220
- }).then(() => Promise.reject(/* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished")));
253
+ });
254
+ const connectionLost = new AbortController();
255
+ const connectionEnded = /* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished");
256
+ connection.then(() => connectionLost.abort(connectionEnded), (error) => connectionLost.abort(error));
257
+ const lost = connection.then(() => Promise.reject(connectionEnded), (error) => Promise.reject(error));
221
258
  lost.catch(() => {});
222
259
  try {
223
260
  let readyTimeout;
@@ -233,25 +270,32 @@ async function snapshot(options) {
233
270
  clearTimeout(readyTimeout);
234
271
  }
235
272
  step("Creating snapshot job");
273
+ const snapshotDeadline = Date.now() + timeoutMs;
274
+ const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1);
236
275
  const job = await createJob({
237
276
  studioUrl: options.studioUrl,
238
277
  token,
239
278
  type: "snapshot",
240
279
  agentId: agent.id,
280
+ instanceId,
241
281
  name,
242
282
  version: packageVersion,
243
- commit: ci.commit
283
+ commit: ci.commit,
284
+ baseId: ci.base?.id,
285
+ timeoutMs: snapshotTimeoutMs(),
286
+ signal: connectionLost.signal
244
287
  });
245
288
  step(`Snapshot job queued: ${job.id}`);
246
289
  const finished = await Promise.race([waitForJob({
247
290
  studioUrl: options.studioUrl,
248
291
  token,
249
292
  id: job.id,
250
- timeoutMs
293
+ timeoutMs: snapshotTimeoutMs(),
294
+ signal: connectionLost.signal
251
295
  }), lost]);
252
296
  if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
253
297
  if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
254
- const result = toResult(options.studioUrl, finished.snapshot, agent.slug);
298
+ const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci);
255
299
  step("Snapshot published");
256
300
  if (options.json) console.log(JSON.stringify(result));
257
301
  if (!options.json) printSummary(result);
@@ -268,6 +312,7 @@ const runner = async ({ values }) => {
268
312
  ...createStudioOptions(values),
269
313
  token: values.token,
270
314
  id: values.id,
315
+ baseId: values.baseId,
271
316
  name: values.name,
272
317
  packageVersion: values.packageVersion,
273
318
  timeout: values.timeout,
@@ -278,4 +323,4 @@ const runner = async ({ values }) => {
278
323
  //#endregion
279
324
  export { runner };
280
325
 
281
- //# sourceMappingURL=snapshot-Cv2GHK1Y.js.map
326
+ //# sourceMappingURL=snapshot-aiwOrbuI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-aiwOrbuI.js","names":["logLevel","logLevelMap"],"sources":["../src/runners/studio/ci.ts","../src/runners/studio/snapshot.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport process from 'node:process'\n\n/**\n * Stable identity for the CI agent a snapshot run registers or reuses.\n */\nexport type CiContext = {\n /**\n * Fed into the machine token, so the same merge or pull request reuses one agent.\n */\n id: string\n /**\n * Agent display name shown in Studio.\n */\n name: string\n /** The commit this run builds, so the next snapshot can diff against it. */\n commit?: string\n /** The branch a pull request merges into, and the `id` runs on that branch register under. */\n base?: { branch: string; id: string }\n}\n\ntype GithubPullRequest = { number: number; head?: { sha?: string }; base?: { ref?: string } }\n\nfunction readGithubPullRequest(eventPath: string | undefined): GithubPullRequest | undefined {\n if (!eventPath) {\n return undefined\n }\n\n try {\n const event = JSON.parse(readFileSync(eventPath, 'utf8')) as { pull_request?: GithubPullRequest }\n return event.pull_request?.number ? event.pull_request : undefined\n } catch {\n return undefined\n }\n}\n\n/** Reproduces GitLab's `CI_COMMIT_REF_SLUG` for a branch it gives no slug of, like a merge request's target. */\nexport function gitlabRefSlug(ref: string): string {\n return ref\n .toLowerCase()\n .replace(/[^a-z0-9]/gu, '-')\n .slice(0, 63)\n .replace(/^-+|-+$/g, '')\n}\n\n/**\n * Detects the CI provider from its environment variables and derives a stable identity from it.\n * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has\n * always registered agents under, so an existing repository keeps reusing its agent.\n */\nexport function detectCi(env: Record<string, string | undefined> = process.env): CiContext | null {\n if (env.GITHUB_ACTIONS) {\n const pullRequest = readGithubPullRequest(env.GITHUB_EVENT_PATH)\n const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? ''\n const branch = env.GITHUB_REF?.startsWith('refs/heads/') ? env.GITHUB_REF.slice('refs/heads/'.length) : undefined\n // A branch run is scoped by its ref, which a pull request number never collides with.\n const scope = pullRequest?.number ?? (branch ? `refs/heads/${branch}` : env.GITHUB_RUN_ID) ?? ''\n const baseBranch = pullRequest?.base?.ref\n\n return {\n id: `gh:${repositoryId}:${scope}`,\n name: `${env.GITHUB_REPOSITORY ?? 'github'}#${pullRequest?.number ?? branch ?? scope}`,\n // The head commit, not the merge commit a pull request run checks out.\n commit: pullRequest?.head?.sha ?? env.GITHUB_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gh:${repositoryId}:refs/heads/${baseBranch}` } : undefined,\n }\n }\n\n if (env.GITLAB_CI) {\n const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? ''\n const baseBranch = env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME\n\n return {\n id: `gl:${env.CI_PROJECT_ID ?? ''}:${scope}`,\n name: `${env.CI_PROJECT_PATH ?? 'gitlab'}#${scope}`,\n commit: env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || env.CI_COMMIT_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gl:${env.CI_PROJECT_ID ?? ''}:${gitlabRefSlug(baseBranch)}` } : undefined,\n }\n }\n\n if (env.BITBUCKET_BUILD_NUMBER) {\n const scope = env.BITBUCKET_PR_ID ?? env.BITBUCKET_BRANCH ?? ''\n\n return { id: `bb:${env.BITBUCKET_REPO_UUID ?? ''}:${scope}`, name: `${env.BITBUCKET_REPO_FULL_NAME ?? 'bitbucket'}#${scope}`, commit: env.BITBUCKET_COMMIT }\n }\n\n if (env.CIRCLECI) {\n // Owner-qualified: two projects with the same name under different CircleCI orgs must not\n // collide on one agent, since registering it purges the other project's live sessions.\n const owner = env.CIRCLE_PROJECT_USERNAME ?? ''\n const project = env.CIRCLE_PROJECT_REPONAME ?? 'circleci'\n const scope = env.CIRCLE_PR_NUMBER ?? env.CIRCLE_BRANCH ?? ''\n\n return { id: `circle:${owner}:${project}:${scope}`, name: `${owner ? `${owner}/` : ''}${project}#${scope}`, commit: env.CIRCLE_SHA1 }\n }\n\n return null\n}\n","import { randomUUID } from 'node:crypto'\nimport { dirname, join } from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { exists, read } from '@internals/utils'\nimport { cliReporter, logLevel as logLevelMap } from '@kubb/core'\nimport { createAgent, createJob, machineTokenFrom, runConnection, waitForJob, type StudioSnapshot, type StudioSnapshotChanges } from '@kubb/studio'\nimport { logBlock } from '../../loggers/output.ts'\nimport { createPlainLogger } from '../../loggers/plainLogger.ts'\nimport setupReporters from '../../loggers/utils.ts'\nimport { type CiContext, detectCi } from './ci.ts'\nimport { createStudioOptions, loadConfigs, run, type SnapshotOptions } from './run.ts'\nimport type { definition } from '../../commands/studio/snapshot.ts'\nimport type { CommandRunner } from 'gunshi'\n\n/**\n * How long to wait for Studio's `studio:ready` acknowledgement, above the client's own 10s\n * handshake-ack timeout.\n */\nconst READY_TIMEOUT_MS = 15_000\n\n/**\n * Longest `--timeout` accepted. Job records expire from Studio's storage after an hour.\n */\nconst MAX_TIMEOUT_SECONDS = 3600\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1'])\n\nasync function findPackageJson(startDirectory: string): Promise<{ name: string; version: string }> {\n let directory = startDirectory\n\n for (;;) {\n const file = join(directory, 'package.json')\n\n if (await exists(file)) {\n const packageJson = JSON.parse(await read(file)) as Partial<{ name: string; version: string }>\n\n if (packageJson.name && packageJson.version) {\n return { name: packageJson.name, version: packageJson.version }\n }\n }\n\n const parent = dirname(directory)\n\n if (parent === directory) {\n break\n }\n\n directory = parent\n }\n\n throw new Error(`No package.json with a name and a version found above ${startDirectory}`)\n}\n\n/** Reads package.json only for whichever of name/version --name and --version did not supply. */\nasync function resolvePackageMetadata(options: SnapshotOptions): Promise<{ name: string; version: string }> {\n if (options.name && options.packageVersion) {\n return { name: options.name, version: options.packageVersion }\n }\n\n const packageMetadata = await findPackageJson(process.cwd())\n\n return { name: options.name ?? packageMetadata.name, version: options.packageVersion ?? packageMetadata.version }\n}\n\nfunction resolveToken(options: SnapshotOptions): string {\n const token = options.token ?? process.env.KUBB_TOKEN\n\n if (!token) {\n throw new Error('An organization CI API key is required. Pass --token or set KUBB_TOKEN.')\n }\n\n return token\n}\n\nfunction resolveCiIdentity(options: SnapshotOptions): CiContext {\n const detected = detectCi()\n // A custom --id drops the detected base, since runs on that branch may use a custom id too.\n const ci = options.id ? { id: options.id, name: detected?.name ?? options.id, commit: detected?.commit } : detected\n\n if (!ci) {\n throw new Error('Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.')\n }\n\n // An explicit --base-id may not name a branch, so it labels the comparison itself.\n const base = options.baseId ? { branch: options.baseId, id: options.baseId } : ci.base\n\n return { ...ci, base: base?.id === ci.id ? undefined : base }\n}\n\n/** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */\nfunction resolveTimeoutMs(options: SnapshotOptions): number {\n const seconds = options.timeout ?? 600\n\n if (!Number.isFinite(seconds) || seconds <= 0) {\n throw new Error('--timeout must be a positive number of seconds')\n }\n\n return Math.min(seconds, MAX_TIMEOUT_SECONDS) * 1000\n}\n\n/** The CI key is a credential, so refuse to send it anywhere but HTTPS or a loopback dev host. */\nfunction assertSecureStudioUrl(studioUrl: string): void {\n const url = new URL(studioUrl)\n\n if (url.protocol !== 'https:' && !LOOPBACK_HOSTS.has(url.hostname)) {\n throw new Error(`Refusing to send the CI API key to ${studioUrl}. Use an https:// Studio URL, or a loopback host for local development.`)\n }\n}\n\nfunction absoluteUrl(studioUrl: string, path: string): string {\n return new URL(path, `${studioUrl}/`).toString()\n}\n\ntype SnapshotResult = {\n id: string\n name: string | null\n version: string | null\n integrity: string | null\n url: string\n snapshotIdUrl: string\n expiresAt: string\n agentUrl: string\n /** What changed since the previous snapshot of this package on this agent. */\n changes?: StudioSnapshotChanges\n /** What differs from the latest snapshot of the base branch, named by `branch`. */\n branchChanges?: StudioSnapshotChanges & { branch: string }\n}\n\nfunction toResult(studioUrl: string, snapshot: StudioSnapshot, agentSlug: string, ci: CiContext): SnapshotResult {\n return {\n id: snapshot.id,\n name: snapshot.name,\n version: snapshot.version,\n integrity: snapshot.integrity,\n url: absoluteUrl(studioUrl, snapshot.url),\n snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),\n expiresAt: snapshot.expiresAt,\n agentUrl: absoluteUrl(studioUrl, `/agents/${agentSlug}`),\n changes: snapshot.changes,\n branchChanges: snapshot.branchChanges && ci.base ? { ...snapshot.branchChanges, branch: ci.base.branch } : undefined,\n }\n}\n\n/** How many files differ, `against` what. */\nfunction describeChanges(changes: StudioSnapshotChanges, against: string): string {\n const { added, changed, removed } = changes\n\n return added.length + changed.length + removed.length\n ? `${added.length} added, ${changed.length} changed, ${removed.length} removed ${against}`\n : `No changes ${against}`\n}\n\n/** One summary line: how many files changed, and since which run. */\nexport function formatChanges(changes: StudioSnapshotChanges): string {\n if (!changes.base) {\n return 'First snapshot'\n }\n\n return describeChanges(changes, `since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}`)\n}\n\n/** One summary line: how the snapshot differs from the base branch's latest one. */\nexport function formatBranchChanges(changes: StudioSnapshotChanges & { branch: string }): string {\n return changes.base ? describeChanges(changes, `against ${changes.branch}`) : `No snapshot of ${changes.branch} to compare with`\n}\n\nfunction printSummary(result: SnapshotResult): void {\n logBlock([\n `${styleText('dim', 'Package'.padEnd(10))} ${result.name ?? '(unnamed)'}@${result.version ?? '0.0.0'}`,\n `${styleText('dim', 'Tarball'.padEnd(10))} ${styleText('cyan', result.url)}`,\n `${styleText('dim', 'Agent'.padEnd(10))} ${result.agentUrl}`,\n ...(result.branchChanges ? [`${styleText('dim', 'Branch'.padEnd(10))} ${formatBranchChanges(result.branchChanges)}`] : []),\n ...(result.changes ? [`${styleText('dim', 'Changes'.padEnd(10))} ${formatChanges(result.changes)}`] : []),\n `${styleText('dim', 'Expires'.padEnd(10))} ${result.expiresAt}`,\n ])\n}\n\n/**\n * Generates a Kubb Studio snapshot from a script: registers or reuses a CI agent, connects it,\n * queues a snapshot job, and polls until the tarball is ready. A snapshot job runs generation and\n * packs the tarball in one step, so this needs no separate generation run.\n */\nexport async function snapshot(options: SnapshotOptions): Promise<void> {\n const timeoutMs = resolveTimeoutMs(options)\n const token = resolveToken(options)\n assertSecureStudioUrl(options.studioUrl)\n const { configPath } = await loadConfigs(options)\n const ci = resolveCiIdentity(options)\n const { name, version: packageVersion } = await resolvePackageMetadata(options)\n const logLevel = logLevelMap[options.logLevel ?? 'info']\n\n // Set before the first `getMachineToken()` call (inside the connection), so the WebSocket\n // session registers under the same machine token `createAgent` just registered with Studio.\n process.env.KUBB_AGENT_SECRET = ci.id\n\n const write = options.json ? (line: string) => console.error(line) : (line: string) => console.log(line)\n const logger = createPlainLogger(write)\n const step = (message: string) => {\n if (logLevel > logLevelMap.silent) {\n write(message)\n }\n }\n\n step('Creating Kubb Studio agent')\n\n const agent = await createAgent({ studioUrl: options.studioUrl, token, name: ci.name, machineToken: machineTokenFrom(ci.id) })\n // Overlapping runs of one pipeline connect under the same CI agent. Naming this process lets the\n // snapshot job run here, on this run's checkout, and not on another run's.\n const instanceId = randomUUID()\n\n const { promise: ready, resolve: markReady } = Promise.withResolvers<void>()\n const shutdown = new AbortController()\n\n const connection = runConnection({\n credentials: { token: agent.token },\n signal: shutdown.signal,\n clientOptions: () => ({\n studioUrl: options.studioUrl,\n instanceId,\n configPath,\n root: process.cwd(),\n version: options.version,\n // Flags only: a script is never prompted and never reuses a saved answer.\n permissions: options.permission,\n loadConfig: async () => (await loadConfigs(options)).config,\n installLogger: async (hooks) => {\n await setupReporters(hooks, { logLevel, reporters: [cliReporter], logger })\n hooks.hook('studio:ready', () => markReady())\n },\n }),\n // A CI agent's token comes from the organization key, so there is no pairing to fall back to.\n onTokenRejected: ({ error }) => Promise.reject(error),\n })\n const connectionLost = new AbortController()\n const connectionEnded = new Error('The Kubb Studio connection ended before the snapshot finished')\n void connection.then(\n () => connectionLost.abort(connectionEnded),\n (error) => connectionLost.abort(error),\n )\n const lost = connection.then(\n () => Promise.reject(connectionEnded),\n (error) => Promise.reject(error),\n )\n void lost.catch(() => {})\n\n try {\n let readyTimeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n ready,\n lost,\n new Promise<never>((_, reject) => {\n readyTimeout = setTimeout(() => reject(new Error('Timed out waiting for Kubb Studio to confirm the agent was ready')), READY_TIMEOUT_MS)\n }),\n ])\n } finally {\n clearTimeout(readyTimeout)\n }\n\n step('Creating snapshot job')\n\n const snapshotDeadline = Date.now() + timeoutMs\n const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1)\n\n const job = await createJob({\n studioUrl: options.studioUrl,\n token,\n type: 'snapshot',\n agentId: agent.id,\n instanceId,\n name,\n version: packageVersion,\n commit: ci.commit,\n baseId: ci.base?.id,\n timeoutMs: snapshotTimeoutMs(),\n signal: connectionLost.signal,\n })\n step(`Snapshot job queued: ${job.id}`)\n const finished = await Promise.race([\n waitForJob({\n studioUrl: options.studioUrl,\n token,\n id: job.id,\n timeoutMs: snapshotTimeoutMs(),\n signal: connectionLost.signal,\n }),\n lost,\n ])\n\n if (finished.status === 'failed') {\n throw new Error(finished.error ?? 'Snapshot job failed')\n }\n\n if (!finished.snapshot) {\n throw new Error('Snapshot job succeeded without a snapshot')\n }\n\n const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci)\n\n step('Snapshot published')\n\n if (options.json) {\n console.log(JSON.stringify(result))\n }\n if (!options.json) {\n printSummary(result)\n }\n } catch (error) {\n step('Snapshot failed')\n throw error\n } finally {\n step('Disconnecting from Kubb Studio')\n // Not awaited: a half-open socket must not hold the run open.\n shutdown.abort()\n }\n}\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options: SnapshotOptions = {\n ...createStudioOptions(values),\n token: values.token,\n id: values.id,\n baseId: values.baseId,\n name: values.name,\n packageVersion: values.packageVersion,\n timeout: values.timeout,\n json: values.json,\n }\n await run(options, () => snapshot(options), { json: options.json })\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  require("./rolldown-runtime-qbf5tadS.cjs");
2
- const require_run = require("./run-DY-3ONFZ.cjs");
2
+ const require_run = require("./run-Dp24WE9v.cjs");
3
3
  //#region src/runners/studio/status.ts
4
4
  const runner = async ({ values }) => {
5
5
  const options = require_run.createStudioOptions(values);
@@ -8,4 +8,4 @@ const runner = async ({ values }) => {
8
8
  //#endregion
9
9
  exports.runner = runner;
10
10
 
11
- //# sourceMappingURL=status-BtXF2YAC.cjs.map
11
+ //# sourceMappingURL=status-CYhqs_P9.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"status-BtXF2YAC.cjs","names":["createStudioOptions","run","status"],"sources":["../src/runners/studio/status.ts"],"sourcesContent":["import type { CommandRunner } from 'gunshi'\nimport type { definition } from '../../commands/studio/status.ts'\nimport { createStudioOptions, run, status } from './run.ts'\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options = createStudioOptions(values)\n await run(options, () => status(options))\n}\n"],"mappings":";;;AAIA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,UAAUA,YAAAA,oBAAoB,MAAM;CAC1C,MAAMC,YAAAA,IAAI,eAAeC,YAAAA,OAAO,OAAO,CAAC;AAC1C"}
1
+ {"version":3,"file":"status-CYhqs_P9.cjs","names":["createStudioOptions","run","status"],"sources":["../src/runners/studio/status.ts"],"sourcesContent":["import type { CommandRunner } from 'gunshi'\nimport type { definition } from '../../commands/studio/status.ts'\nimport { createStudioOptions, run, status } from './run.ts'\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options = createStudioOptions(values)\n await run(options, () => status(options))\n}\n"],"mappings":";;;AAIA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,UAAUA,YAAAA,oBAAoB,MAAM;CAC1C,MAAMC,YAAAA,IAAI,eAAeC,YAAAA,OAAO,OAAO,CAAC;AAC1C"}
@@ -1,5 +1,5 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
- import { a as run, n as createStudioOptions, o as status } from "./run-DOOtoHGQ.js";
2
+ import { a as run, n as createStudioOptions, o as status } from "./run-D6QwS31H.js";
3
3
  //#region src/runners/studio/status.ts
4
4
  const runner = async ({ values }) => {
5
5
  const options = createStudioOptions(values);
@@ -8,4 +8,4 @@ const runner = async ({ values }) => {
8
8
  //#endregion
9
9
  export { runner };
10
10
 
11
- //# sourceMappingURL=status-DT1nKRUb.js.map
11
+ //# sourceMappingURL=status-CkKR5TPs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"status-DT1nKRUb.js","names":[],"sources":["../src/runners/studio/status.ts"],"sourcesContent":["import type { CommandRunner } from 'gunshi'\nimport type { definition } from '../../commands/studio/status.ts'\nimport { createStudioOptions, run, status } from './run.ts'\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options = createStudioOptions(values)\n await run(options, () => status(options))\n}\n"],"mappings":";;;AAIA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,UAAU,oBAAoB,MAAM;CAC1C,MAAM,IAAI,eAAe,OAAO,OAAO,CAAC;AAC1C"}
1
+ {"version":3,"file":"status-CkKR5TPs.js","names":[],"sources":["../src/runners/studio/status.ts"],"sourcesContent":["import type { CommandRunner } from 'gunshi'\nimport type { definition } from '../../commands/studio/status.ts'\nimport { createStudioOptions, run, status } from './run.ts'\n\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n const options = createStudioOptions(values)\n await run(options, () => status(options))\n}\n"],"mappings":";;;AAIA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,UAAU,oBAAoB,MAAM;CAC1C,MAAM,IAAI,eAAe,OAAO,OAAO,CAAC;AAC1C"}
@@ -75,6 +75,11 @@ const definition$2 = define({
75
75
  type: "string",
76
76
  description: "Stable identity for the CI agent, such as a pull request. Auto-detected on GitHub Actions, GitLab CI, Bitbucket Pipelines and CircleCI"
77
77
  },
78
+ baseId: {
79
+ type: "string",
80
+ description: "Identity of another CI agent, such as the base branch's, to also compare this snapshot with. Auto-detected on GitHub pull requests and GitLab merge requests",
81
+ toKebab: true
82
+ },
78
83
  name: {
79
84
  type: "string",
80
85
  description: "Package name for the generated tarball. Defaults to the name in package.json"
@@ -138,18 +143,18 @@ const definition = define({
138
143
  ...logLevelArg
139
144
  },
140
145
  async run(ctx) {
141
- const { runner } = await import("./connect-IObjkkpy.js");
146
+ const { runner } = await import("./connect-BP2OiMvo.js");
142
147
  await runner(ctx);
143
148
  },
144
149
  subCommands: {
145
- connect: lazy(async () => (await import("./connect-IObjkkpy.js")).runner, definition$5),
146
- login: lazy(async () => (await import("./login-CA_xoFp7.js")).runner, definition$4),
147
- logout: lazy(async () => (await import("./logout-BRZyHSgy.js")).runner, definition$3),
148
- status: lazy(async () => (await import("./status-DT1nKRUb.js")).runner, definition$1),
149
- snapshot: lazy(async () => (await import("./snapshot-Cv2GHK1Y.js")).runner, definition$2)
150
+ connect: lazy(async () => (await import("./connect-BP2OiMvo.js")).runner, definition$5),
151
+ login: lazy(async () => (await import("./login-AEDO32Tf.js")).runner, definition$4),
152
+ logout: lazy(async () => (await import("./logout-Bw7zyqib.js")).runner, definition$3),
153
+ status: lazy(async () => (await import("./status-CkKR5TPs.js")).runner, definition$1),
154
+ snapshot: lazy(async () => (await import("./snapshot-aiwOrbuI.js")).runner, definition$2)
150
155
  }
151
156
  });
152
157
  //#endregion
153
158
  export { definition };
154
159
 
155
- //# sourceMappingURL=studio-CwZBPZWN.js.map
160
+ //# sourceMappingURL=studio-C2ex3VFM.js.map