@valbuild/cli 0.136.0 → 0.136.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @valbuild/cli
2
2
 
3
+ ## 0.136.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`f76cd56`](https://github.com/valbuild/val/commit/f76cd56b9f1c1bcc0a608a7a4de24b1e776adccb)]:
8
+ - @valbuild/server@0.136.2
9
+ - @valbuild/shared@0.136.2
10
+ - @valbuild/language-server@0.136.2
11
+
12
+ ## 0.136.1
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [[`e673b43`](https://github.com/valbuild/val/commit/e673b43feaf48b7f30881d6786c858a0cb6a44a2), [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1), [`4450be5`](https://github.com/valbuild/val/commit/4450be570ec3a66d7d98ba273c333ca2d2f163d9), [`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef)]:
17
+ - @valbuild/shared@0.136.1
18
+ - @valbuild/server@0.136.1
19
+ - @valbuild/core@0.136.1
20
+ - @valbuild/language-server@0.136.1
21
+
3
22
  ## 0.136.0
4
23
 
5
24
  ### Patch Changes
@@ -15,8 +15,6 @@ var JSZip = require('jszip');
15
15
  var readline = require('readline');
16
16
  var crypto = require('crypto');
17
17
  var zlib = require('zlib');
18
- var zod = require('zod');
19
- var zodValidationError = require('zod-validation-error');
20
18
 
21
19
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
22
20
 
@@ -2419,127 +2417,6 @@ function errorDetailsOf(body) {
2419
2417
  return undefined;
2420
2418
  }
2421
2419
 
2422
- /**
2423
- * Reading what content answers.
2424
- *
2425
- * Every type here comes from `contentApi.ts`, which is a copy of the
2426
- * service's own `Api.ts` - so the shapes are not re-derived from what this end
2427
- * wishes it received, and a copy brought up to date fails to compile wherever
2428
- * this CLI has not caught up.
2429
- *
2430
- * The schemas are the runtime half, and they are needed as well as the types:
2431
- * what arrives is JSON over HTTP from a service that versions separately, so a
2432
- * type alone asserts a shape rather than checking one. Each schema is
2433
- * annotated with the copied type it must produce (`z.ZodType<DeclareResponse>`
2434
- * and so on), which is what ties the two halves together - a schema that drifts
2435
- * from the copy does not compile, and an answer that drifts from the schema
2436
- * does not parse. `ValOpsHttp` in `@valbuild/server` reads every other content
2437
- * route the same way.
2438
- *
2439
- * Nothing here is laxer than the copy. An optional field is optional because
2440
- * the service says so; making a required one optional here would hide exactly
2441
- * the drift this file exists to catch.
2442
- */
2443
-
2444
- /** An artifact, as it is declared: by key, by hash, by size. */
2445
-
2446
- /** Permission to write one artifact's bytes to object storage. */
2447
-
2448
- const publishState = zod.z.enum(["awaiting-artifacts", "ready", "verified", "live", "failed", "expired"]);
2449
- const publishProblem = zod.z.object({
2450
- code: zod.z.string(),
2451
- message: zod.z.string(),
2452
- hint: zod.z.string().optional(),
2453
- keys: zod.z.array(zod.z.string()).optional()
2454
- });
2455
- const uploadSlot = zod.z.object({
2456
- key: zod.z.string(),
2457
- url: zod.z.string(),
2458
- method: zod.z.literal("PUT"),
2459
- headers: zod.z.record(zod.z.string(), zod.z.string()),
2460
- expiresAt: zod.z.string()
2461
- });
2462
- const declareResponse = zod.z.object({
2463
- publishId: zod.z.string(),
2464
- state: publishState,
2465
- project: zod.z.object({
2466
- publicProjectId: zod.z.string(),
2467
- siteUrl: zod.z.string().nullable()
2468
- }),
2469
- uploads: zod.z.array(uploadSlot),
2470
- have: zod.z.array(zod.z.string())
2471
- });
2472
- const artifactsResponse = zod.z.object({
2473
- state: publishState,
2474
- problems: zod.z.array(publishProblem)
2475
- });
2476
- const verifyResponse = zod.z.object({
2477
- state: publishState,
2478
- ok: zod.z.boolean(),
2479
- previewUrl: zod.z.string().nullable(),
2480
- problems: zod.z.array(publishProblem)
2481
- });
2482
- const promoteResponse = zod.z.object({
2483
- state: publishState,
2484
- url: zod.z.string().nullable(),
2485
- commit: zod.z.string().nullable()
2486
- });
2487
- const statusResponse = zod.z.object({
2488
- publishId: zod.z.string(),
2489
- state: publishState,
2490
- buildHash: zod.z.string(),
2491
- missing: zod.z.array(zod.z.string()),
2492
- problems: zod.z.array(publishProblem)
2493
- });
2494
- const publishTokenResponse = zod.z.object({
2495
- token: zod.z.string(),
2496
- expiresAt: zod.z.string().nullable(),
2497
- publicProjectId: zod.z.string(),
2498
- productionUrl: zod.z.string().nullable()
2499
- });
2500
-
2501
- /**
2502
- * Content answered, and the answer was not one this CLI can act on.
2503
- *
2504
- * Separate from a refusal, which is content saying no in a sentence meant for
2505
- * a person. This is two programs disagreeing - the copy in `contentApi.ts` and
2506
- * the service that has moved on from it - and the two want opposite things
2507
- * said about them.
2508
- */
2509
- class PublishProtocolError extends Error {
2510
- constructor(message) {
2511
- super(message);
2512
- this.name = "PublishProtocolError";
2513
- }
2514
- }
2515
- const parseDeclare = body => parse(declareResponse, body, "POST /v1/publish");
2516
- const parseArtifacts = body => parse(artifactsResponse, body, "POST /v1/publish/{id}/artifacts");
2517
- const parseVerify = body => parse(verifyResponse, body, "POST /v1/publish/{id}/verify");
2518
- const parsePromote = body => parse(promoteResponse, body, "POST /v1/publish/{id}/promote");
2519
- const parseStatus = body => parse(statusResponse, body, "GET /v1/publish/{id}");
2520
- const parsePublishToken = (body, call) => parse(publishTokenResponse, body, call);
2521
-
2522
- /**
2523
- * The `details` of a refusal, when it carries a list of problems.
2524
- *
2525
- * Every non-2xx is `{ statusCode, message, details? }`, and the publish routes
2526
- * put `PublishProblem[]` in `details` - a declaration is answered with EVERY
2527
- * problem rather than the first, because finding them one round trip at a time
2528
- * is how a publish takes six attempts. Anything else in there is somebody
2529
- * else's `details` and is left to the caller's own message.
2530
- */
2531
- function parseProblems(details) {
2532
- const parsed = zod.z.array(publishProblem).safeParse(details);
2533
- return parsed.success ? parsed.data : [];
2534
- }
2535
- function parse(schema, body, call) {
2536
- const parsed = schema.safeParse(body);
2537
- if (!parsed.success) {
2538
- throw new PublishProtocolError(`${call} answered with something this CLI cannot read: ${zodValidationError.fromError(parsed.error).toString()}`);
2539
- }
2540
- return parsed.data;
2541
- }
2542
-
2543
2420
  function createPublishClient(options) {
2544
2421
  const {
2545
2422
  host,
@@ -2553,28 +2430,28 @@ function createPublishClient(options) {
2553
2430
  };
2554
2431
  const publishUrl = (publishId, step) => `${host}/v1/publish/${encodeURIComponent(publishId)}${step ? `/${step}` : ""}`;
2555
2432
  return {
2556
- declare: async body => parseDeclare(await postJson({
2433
+ declare: async body => internal.parseDeclare(await postJson({
2557
2434
  url: `${host}/v1/publish`,
2558
2435
  headers,
2559
2436
  body,
2560
2437
  fetchImpl
2561
2438
  })),
2562
- confirmArtifacts: async publishId => parseArtifacts(await postJson({
2439
+ confirmArtifacts: async publishId => internal.parseArtifacts(await postJson({
2563
2440
  url: publishUrl(publishId, "artifacts"),
2564
2441
  headers,
2565
2442
  fetchImpl
2566
2443
  })),
2567
- verify: async publishId => parseVerify(await postJson({
2444
+ verify: async publishId => internal.parseVerify(await postJson({
2568
2445
  url: publishUrl(publishId, "verify"),
2569
2446
  headers,
2570
2447
  fetchImpl
2571
2448
  })),
2572
- promote: async publishId => parsePromote(await postJson({
2449
+ promote: async publishId => internal.parsePromote(await postJson({
2573
2450
  url: publishUrl(publishId, "promote"),
2574
2451
  headers,
2575
2452
  fetchImpl
2576
2453
  })),
2577
- status: async publishId => parseStatus(await getJson({
2454
+ status: async publishId => internal.parseStatus(await getJson({
2578
2455
  url: publishUrl(publishId),
2579
2456
  headers,
2580
2457
  fetchImpl
@@ -2808,9 +2685,9 @@ async function exchangePersonalAccessToken(options) {
2808
2685
  }
2809
2686
  let exchanged;
2810
2687
  try {
2811
- exchanged = parsePublishToken(body, `POST ${url}`);
2688
+ exchanged = internal.parsePublishToken(body, `POST ${url}`);
2812
2689
  } catch (err) {
2813
- if (err instanceof PublishProtocolError) {
2690
+ if (err instanceof internal.PublishProtocolError) {
2814
2691
  return {
2815
2692
  status: "error",
2816
2693
  message: err.message
@@ -3166,7 +3043,7 @@ async function declare(client, body) {
3166
3043
  return {
3167
3044
  status: "refused",
3168
3045
  message: err.message,
3169
- problems: parseProblems(err.details)
3046
+ problems: internal.parseProblems(err.details)
3170
3047
  };
3171
3048
  }
3172
3049
  throw err;
@@ -3186,7 +3063,7 @@ async function confirmArtifacts(client, publishId) {
3186
3063
  // Both are fixed by fresh slots and sending again.
3187
3064
  return {
3188
3065
  status: "mismatch",
3189
- problems: parseProblems(err.details)
3066
+ problems: internal.parseProblems(err.details)
3190
3067
  };
3191
3068
  }
3192
3069
  throw err;
@@ -3230,7 +3107,7 @@ async function promote(client, publishId) {
3230
3107
  return {
3231
3108
  status: "stale",
3232
3109
  message: err.message + (head ? ` The branch is at ${head.slice(0, 7)} now.` : ""),
3233
- problems: parseProblems(err.details)
3110
+ problems: internal.parseProblems(err.details)
3234
3111
  };
3235
3112
  }
3236
3113
  throw err;
@@ -3265,7 +3142,7 @@ async function uploadAll(args) {
3265
3142
  if (file === undefined) {
3266
3143
  // Content asked for a key this build does not have. Declaring again
3267
3144
  // cannot fix that, so it is not a round; it is a bug on one side.
3268
- throw new PublishProtocolError(`Content asked for an artifact this build does not have: ${slot.key}`);
3145
+ throw new internal.PublishProtocolError(`Content asked for an artifact this build does not have: ${slot.key}`);
3269
3146
  }
3270
3147
  for (let attempt = 1;; attempt++) {
3271
3148
  try {
@@ -3313,7 +3190,7 @@ function errorFrom(err) {
3313
3190
  message: err.statusCode === 401 || err.statusCode === 403 ? `${err.message}\n\nThe credential was refused. A project token can be revoked, and only a token carrying val:publish may publish.` : `${err.message}${detail ? `\n${detail}` : ""}`
3314
3191
  };
3315
3192
  }
3316
- if (err instanceof UploadError || err instanceof PublishProtocolError) {
3193
+ if (err instanceof UploadError || err instanceof internal.PublishProtocolError) {
3317
3194
  return {
3318
3195
  status: "error",
3319
3196
  message: err.message
@@ -15,8 +15,6 @@ var JSZip = require('jszip');
15
15
  var readline = require('readline');
16
16
  var crypto = require('crypto');
17
17
  var zlib = require('zlib');
18
- var zod = require('zod');
19
- var zodValidationError = require('zod-validation-error');
20
18
 
21
19
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
22
20
 
@@ -2419,127 +2417,6 @@ function errorDetailsOf(body) {
2419
2417
  return undefined;
2420
2418
  }
2421
2419
 
2422
- /**
2423
- * Reading what content answers.
2424
- *
2425
- * Every type here comes from `contentApi.ts`, which is a copy of the
2426
- * service's own `Api.ts` - so the shapes are not re-derived from what this end
2427
- * wishes it received, and a copy brought up to date fails to compile wherever
2428
- * this CLI has not caught up.
2429
- *
2430
- * The schemas are the runtime half, and they are needed as well as the types:
2431
- * what arrives is JSON over HTTP from a service that versions separately, so a
2432
- * type alone asserts a shape rather than checking one. Each schema is
2433
- * annotated with the copied type it must produce (`z.ZodType<DeclareResponse>`
2434
- * and so on), which is what ties the two halves together - a schema that drifts
2435
- * from the copy does not compile, and an answer that drifts from the schema
2436
- * does not parse. `ValOpsHttp` in `@valbuild/server` reads every other content
2437
- * route the same way.
2438
- *
2439
- * Nothing here is laxer than the copy. An optional field is optional because
2440
- * the service says so; making a required one optional here would hide exactly
2441
- * the drift this file exists to catch.
2442
- */
2443
-
2444
- /** An artifact, as it is declared: by key, by hash, by size. */
2445
-
2446
- /** Permission to write one artifact's bytes to object storage. */
2447
-
2448
- const publishState = zod.z.enum(["awaiting-artifacts", "ready", "verified", "live", "failed", "expired"]);
2449
- const publishProblem = zod.z.object({
2450
- code: zod.z.string(),
2451
- message: zod.z.string(),
2452
- hint: zod.z.string().optional(),
2453
- keys: zod.z.array(zod.z.string()).optional()
2454
- });
2455
- const uploadSlot = zod.z.object({
2456
- key: zod.z.string(),
2457
- url: zod.z.string(),
2458
- method: zod.z.literal("PUT"),
2459
- headers: zod.z.record(zod.z.string(), zod.z.string()),
2460
- expiresAt: zod.z.string()
2461
- });
2462
- const declareResponse = zod.z.object({
2463
- publishId: zod.z.string(),
2464
- state: publishState,
2465
- project: zod.z.object({
2466
- publicProjectId: zod.z.string(),
2467
- siteUrl: zod.z.string().nullable()
2468
- }),
2469
- uploads: zod.z.array(uploadSlot),
2470
- have: zod.z.array(zod.z.string())
2471
- });
2472
- const artifactsResponse = zod.z.object({
2473
- state: publishState,
2474
- problems: zod.z.array(publishProblem)
2475
- });
2476
- const verifyResponse = zod.z.object({
2477
- state: publishState,
2478
- ok: zod.z.boolean(),
2479
- previewUrl: zod.z.string().nullable(),
2480
- problems: zod.z.array(publishProblem)
2481
- });
2482
- const promoteResponse = zod.z.object({
2483
- state: publishState,
2484
- url: zod.z.string().nullable(),
2485
- commit: zod.z.string().nullable()
2486
- });
2487
- const statusResponse = zod.z.object({
2488
- publishId: zod.z.string(),
2489
- state: publishState,
2490
- buildHash: zod.z.string(),
2491
- missing: zod.z.array(zod.z.string()),
2492
- problems: zod.z.array(publishProblem)
2493
- });
2494
- const publishTokenResponse = zod.z.object({
2495
- token: zod.z.string(),
2496
- expiresAt: zod.z.string().nullable(),
2497
- publicProjectId: zod.z.string(),
2498
- productionUrl: zod.z.string().nullable()
2499
- });
2500
-
2501
- /**
2502
- * Content answered, and the answer was not one this CLI can act on.
2503
- *
2504
- * Separate from a refusal, which is content saying no in a sentence meant for
2505
- * a person. This is two programs disagreeing - the copy in `contentApi.ts` and
2506
- * the service that has moved on from it - and the two want opposite things
2507
- * said about them.
2508
- */
2509
- class PublishProtocolError extends Error {
2510
- constructor(message) {
2511
- super(message);
2512
- this.name = "PublishProtocolError";
2513
- }
2514
- }
2515
- const parseDeclare = body => parse(declareResponse, body, "POST /v1/publish");
2516
- const parseArtifacts = body => parse(artifactsResponse, body, "POST /v1/publish/{id}/artifacts");
2517
- const parseVerify = body => parse(verifyResponse, body, "POST /v1/publish/{id}/verify");
2518
- const parsePromote = body => parse(promoteResponse, body, "POST /v1/publish/{id}/promote");
2519
- const parseStatus = body => parse(statusResponse, body, "GET /v1/publish/{id}");
2520
- const parsePublishToken = (body, call) => parse(publishTokenResponse, body, call);
2521
-
2522
- /**
2523
- * The `details` of a refusal, when it carries a list of problems.
2524
- *
2525
- * Every non-2xx is `{ statusCode, message, details? }`, and the publish routes
2526
- * put `PublishProblem[]` in `details` - a declaration is answered with EVERY
2527
- * problem rather than the first, because finding them one round trip at a time
2528
- * is how a publish takes six attempts. Anything else in there is somebody
2529
- * else's `details` and is left to the caller's own message.
2530
- */
2531
- function parseProblems(details) {
2532
- const parsed = zod.z.array(publishProblem).safeParse(details);
2533
- return parsed.success ? parsed.data : [];
2534
- }
2535
- function parse(schema, body, call) {
2536
- const parsed = schema.safeParse(body);
2537
- if (!parsed.success) {
2538
- throw new PublishProtocolError(`${call} answered with something this CLI cannot read: ${zodValidationError.fromError(parsed.error).toString()}`);
2539
- }
2540
- return parsed.data;
2541
- }
2542
-
2543
2420
  function createPublishClient(options) {
2544
2421
  const {
2545
2422
  host,
@@ -2553,28 +2430,28 @@ function createPublishClient(options) {
2553
2430
  };
2554
2431
  const publishUrl = (publishId, step) => `${host}/v1/publish/${encodeURIComponent(publishId)}${step ? `/${step}` : ""}`;
2555
2432
  return {
2556
- declare: async body => parseDeclare(await postJson({
2433
+ declare: async body => internal.parseDeclare(await postJson({
2557
2434
  url: `${host}/v1/publish`,
2558
2435
  headers,
2559
2436
  body,
2560
2437
  fetchImpl
2561
2438
  })),
2562
- confirmArtifacts: async publishId => parseArtifacts(await postJson({
2439
+ confirmArtifacts: async publishId => internal.parseArtifacts(await postJson({
2563
2440
  url: publishUrl(publishId, "artifacts"),
2564
2441
  headers,
2565
2442
  fetchImpl
2566
2443
  })),
2567
- verify: async publishId => parseVerify(await postJson({
2444
+ verify: async publishId => internal.parseVerify(await postJson({
2568
2445
  url: publishUrl(publishId, "verify"),
2569
2446
  headers,
2570
2447
  fetchImpl
2571
2448
  })),
2572
- promote: async publishId => parsePromote(await postJson({
2449
+ promote: async publishId => internal.parsePromote(await postJson({
2573
2450
  url: publishUrl(publishId, "promote"),
2574
2451
  headers,
2575
2452
  fetchImpl
2576
2453
  })),
2577
- status: async publishId => parseStatus(await getJson({
2454
+ status: async publishId => internal.parseStatus(await getJson({
2578
2455
  url: publishUrl(publishId),
2579
2456
  headers,
2580
2457
  fetchImpl
@@ -2808,9 +2685,9 @@ async function exchangePersonalAccessToken(options) {
2808
2685
  }
2809
2686
  let exchanged;
2810
2687
  try {
2811
- exchanged = parsePublishToken(body, `POST ${url}`);
2688
+ exchanged = internal.parsePublishToken(body, `POST ${url}`);
2812
2689
  } catch (err) {
2813
- if (err instanceof PublishProtocolError) {
2690
+ if (err instanceof internal.PublishProtocolError) {
2814
2691
  return {
2815
2692
  status: "error",
2816
2693
  message: err.message
@@ -3166,7 +3043,7 @@ async function declare(client, body) {
3166
3043
  return {
3167
3044
  status: "refused",
3168
3045
  message: err.message,
3169
- problems: parseProblems(err.details)
3046
+ problems: internal.parseProblems(err.details)
3170
3047
  };
3171
3048
  }
3172
3049
  throw err;
@@ -3186,7 +3063,7 @@ async function confirmArtifacts(client, publishId) {
3186
3063
  // Both are fixed by fresh slots and sending again.
3187
3064
  return {
3188
3065
  status: "mismatch",
3189
- problems: parseProblems(err.details)
3066
+ problems: internal.parseProblems(err.details)
3190
3067
  };
3191
3068
  }
3192
3069
  throw err;
@@ -3230,7 +3107,7 @@ async function promote(client, publishId) {
3230
3107
  return {
3231
3108
  status: "stale",
3232
3109
  message: err.message + (head ? ` The branch is at ${head.slice(0, 7)} now.` : ""),
3233
- problems: parseProblems(err.details)
3110
+ problems: internal.parseProblems(err.details)
3234
3111
  };
3235
3112
  }
3236
3113
  throw err;
@@ -3265,7 +3142,7 @@ async function uploadAll(args) {
3265
3142
  if (file === undefined) {
3266
3143
  // Content asked for a key this build does not have. Declaring again
3267
3144
  // cannot fix that, so it is not a round; it is a bug on one side.
3268
- throw new PublishProtocolError(`Content asked for an artifact this build does not have: ${slot.key}`);
3145
+ throw new internal.PublishProtocolError(`Content asked for an artifact this build does not have: ${slot.key}`);
3269
3146
  }
3270
3147
  for (let attempt = 1;; attempt++) {
3271
3148
  try {
@@ -3313,7 +3190,7 @@ function errorFrom(err) {
3313
3190
  message: err.statusCode === 401 || err.statusCode === 403 ? `${err.message}\n\nThe credential was refused. A project token can be revoked, and only a token carrying val:publish may publish.` : `${err.message}${detail ? `\n${detail}` : ""}`
3314
3191
  };
3315
3192
  }
3316
- if (err instanceof UploadError || err instanceof PublishProtocolError) {
3193
+ if (err instanceof UploadError || err instanceof internal.PublishProtocolError) {
3317
3194
  return {
3318
3195
  status: "error",
3319
3196
  message: err.message
@@ -6,15 +6,13 @@ import { glob } from 'fast-glob';
6
6
  import { Internal, DEFAULT_VAL_REMOTE_HOST, DEFAULT_CONTENT_HOST } from '@valbuild/core';
7
7
  import { createPrettierFormatter, createService, createValModuleFileInspector, fixHandlers, createFixPatch, getModulePathRange, findJsonEntryFilePath, createJsonEntryPathMap, createModulePathMap, findAndEvalValConfigFile, getSettings, uploadRemoteFile, createDefaultValFSHost, startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, loadValModules, safeReadGit, ValOpsHttp, ValOpsFS, getPersonalAccessTokenPath, parsePersonalAccessTokenFile, formatPatchSourceError } from '@valbuild/server';
8
8
  import fs from 'fs/promises';
9
- import { resolveSchemaSourceFixes } from '@valbuild/shared/internal';
9
+ import { resolveSchemaSourceFixes, parseDeclare, parseArtifacts, parseVerify, parsePromote, parseStatus, parsePublishToken, PublishProtocolError, parseProblems } from '@valbuild/shared/internal';
10
10
  import fs$1 from 'fs';
11
11
  import ts from 'typescript';
12
12
  import JSZip from 'jszip';
13
13
  import readline from 'readline';
14
14
  import crypto from 'crypto';
15
15
  import zlib from 'zlib';
16
- import { z } from 'zod';
17
- import { fromError } from 'zod-validation-error';
18
16
 
19
17
  function error(message) {
20
18
  console.error(chalk.red("❌Error: ") + message);
@@ -2385,127 +2383,6 @@ function errorDetailsOf(body) {
2385
2383
  return undefined;
2386
2384
  }
2387
2385
 
2388
- /**
2389
- * Reading what content answers.
2390
- *
2391
- * Every type here comes from `contentApi.ts`, which is a copy of the
2392
- * service's own `Api.ts` - so the shapes are not re-derived from what this end
2393
- * wishes it received, and a copy brought up to date fails to compile wherever
2394
- * this CLI has not caught up.
2395
- *
2396
- * The schemas are the runtime half, and they are needed as well as the types:
2397
- * what arrives is JSON over HTTP from a service that versions separately, so a
2398
- * type alone asserts a shape rather than checking one. Each schema is
2399
- * annotated with the copied type it must produce (`z.ZodType<DeclareResponse>`
2400
- * and so on), which is what ties the two halves together - a schema that drifts
2401
- * from the copy does not compile, and an answer that drifts from the schema
2402
- * does not parse. `ValOpsHttp` in `@valbuild/server` reads every other content
2403
- * route the same way.
2404
- *
2405
- * Nothing here is laxer than the copy. An optional field is optional because
2406
- * the service says so; making a required one optional here would hide exactly
2407
- * the drift this file exists to catch.
2408
- */
2409
-
2410
- /** An artifact, as it is declared: by key, by hash, by size. */
2411
-
2412
- /** Permission to write one artifact's bytes to object storage. */
2413
-
2414
- const publishState = z.enum(["awaiting-artifacts", "ready", "verified", "live", "failed", "expired"]);
2415
- const publishProblem = z.object({
2416
- code: z.string(),
2417
- message: z.string(),
2418
- hint: z.string().optional(),
2419
- keys: z.array(z.string()).optional()
2420
- });
2421
- const uploadSlot = z.object({
2422
- key: z.string(),
2423
- url: z.string(),
2424
- method: z.literal("PUT"),
2425
- headers: z.record(z.string(), z.string()),
2426
- expiresAt: z.string()
2427
- });
2428
- const declareResponse = z.object({
2429
- publishId: z.string(),
2430
- state: publishState,
2431
- project: z.object({
2432
- publicProjectId: z.string(),
2433
- siteUrl: z.string().nullable()
2434
- }),
2435
- uploads: z.array(uploadSlot),
2436
- have: z.array(z.string())
2437
- });
2438
- const artifactsResponse = z.object({
2439
- state: publishState,
2440
- problems: z.array(publishProblem)
2441
- });
2442
- const verifyResponse = z.object({
2443
- state: publishState,
2444
- ok: z.boolean(),
2445
- previewUrl: z.string().nullable(),
2446
- problems: z.array(publishProblem)
2447
- });
2448
- const promoteResponse = z.object({
2449
- state: publishState,
2450
- url: z.string().nullable(),
2451
- commit: z.string().nullable()
2452
- });
2453
- const statusResponse = z.object({
2454
- publishId: z.string(),
2455
- state: publishState,
2456
- buildHash: z.string(),
2457
- missing: z.array(z.string()),
2458
- problems: z.array(publishProblem)
2459
- });
2460
- const publishTokenResponse = z.object({
2461
- token: z.string(),
2462
- expiresAt: z.string().nullable(),
2463
- publicProjectId: z.string(),
2464
- productionUrl: z.string().nullable()
2465
- });
2466
-
2467
- /**
2468
- * Content answered, and the answer was not one this CLI can act on.
2469
- *
2470
- * Separate from a refusal, which is content saying no in a sentence meant for
2471
- * a person. This is two programs disagreeing - the copy in `contentApi.ts` and
2472
- * the service that has moved on from it - and the two want opposite things
2473
- * said about them.
2474
- */
2475
- class PublishProtocolError extends Error {
2476
- constructor(message) {
2477
- super(message);
2478
- this.name = "PublishProtocolError";
2479
- }
2480
- }
2481
- const parseDeclare = body => parse(declareResponse, body, "POST /v1/publish");
2482
- const parseArtifacts = body => parse(artifactsResponse, body, "POST /v1/publish/{id}/artifacts");
2483
- const parseVerify = body => parse(verifyResponse, body, "POST /v1/publish/{id}/verify");
2484
- const parsePromote = body => parse(promoteResponse, body, "POST /v1/publish/{id}/promote");
2485
- const parseStatus = body => parse(statusResponse, body, "GET /v1/publish/{id}");
2486
- const parsePublishToken = (body, call) => parse(publishTokenResponse, body, call);
2487
-
2488
- /**
2489
- * The `details` of a refusal, when it carries a list of problems.
2490
- *
2491
- * Every non-2xx is `{ statusCode, message, details? }`, and the publish routes
2492
- * put `PublishProblem[]` in `details` - a declaration is answered with EVERY
2493
- * problem rather than the first, because finding them one round trip at a time
2494
- * is how a publish takes six attempts. Anything else in there is somebody
2495
- * else's `details` and is left to the caller's own message.
2496
- */
2497
- function parseProblems(details) {
2498
- const parsed = z.array(publishProblem).safeParse(details);
2499
- return parsed.success ? parsed.data : [];
2500
- }
2501
- function parse(schema, body, call) {
2502
- const parsed = schema.safeParse(body);
2503
- if (!parsed.success) {
2504
- throw new PublishProtocolError(`${call} answered with something this CLI cannot read: ${fromError(parsed.error).toString()}`);
2505
- }
2506
- return parsed.data;
2507
- }
2508
-
2509
2386
  function createPublishClient(options) {
2510
2387
  const {
2511
2388
  host,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.136.0",
4
+ "version": "0.136.2",
5
5
  "description": "Val CLI tools",
6
6
  "repository": {
7
7
  "type": "git",
@@ -26,11 +26,11 @@
26
26
  "picocolors": "^1.1.1",
27
27
  "zod": "^4.4.3",
28
28
  "zod-validation-error": "^5.0.0",
29
- "@valbuild/core": "0.136.0",
29
+ "@valbuild/core": "0.136.1",
30
30
  "@valbuild/eslint-plugin": "0.125.0",
31
- "@valbuild/language-server": "0.136.0",
32
- "@valbuild/server": "0.136.0",
33
- "@valbuild/shared": "0.136.0"
31
+ "@valbuild/language-server": "0.136.2",
32
+ "@valbuild/server": "0.136.2",
33
+ "@valbuild/shared": "0.136.2"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "prettier": "*",
@@ -2,7 +2,7 @@ import crypto from "crypto";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import zlib from "zlib";
5
- import { DeclaredArtifact } from "./protocol";
5
+ import { DeclaredArtifact } from "@valbuild/shared/internal";
6
6
 
7
7
  /**
8
8
  * Where an artifact's bytes are, and what it is called on the wire.
@@ -13,7 +13,7 @@ import {
13
13
  parsePromote,
14
14
  parseStatus,
15
15
  parseVerify,
16
- } from "./protocol";
16
+ } from "@valbuild/shared/internal";
17
17
 
18
18
  export type PublishClient = {
19
19
  declare(body: DeclareBody): Promise<DeclareResponse>;
@@ -4,7 +4,10 @@ import {
4
4
  parsePersonalAccessTokenFile,
5
5
  } from "@valbuild/server";
6
6
  import { ContentHostError, getContentHost, postJson } from "./contentHost";
7
- import { PublishProtocolError, parsePublishToken } from "./protocol";
7
+ import {
8
+ PublishProtocolError,
9
+ parsePublishToken,
10
+ } from "@valbuild/shared/internal";
8
11
 
9
12
  /**
10
13
  * The credential `val publish` presents to content, and where it came from.
@@ -1,6 +1,6 @@
1
1
  import crypto from "crypto";
2
2
  import http from "http";
3
- import { PublishProblem } from "./contentApi";
3
+ import { PublishProblem } from "@valbuild/shared/internal";
4
4
 
5
5
  /**
6
6
  * A local stand-in for the publish API on content.val.build.
@@ -6,7 +6,7 @@ import {
6
6
  parsePublishToken,
7
7
  parseStatus,
8
8
  parseVerify,
9
- } from "./protocol";
9
+ } from "@valbuild/shared/internal";
10
10
 
11
11
  /**
12
12
  * What content actually answers, run through the parsers that read it.
@@ -27,7 +27,7 @@ import {
27
27
  UploadSlot,
28
28
  VerifyResponse,
29
29
  parseProblems,
30
- } from "./protocol";
30
+ } from "@valbuild/shared/internal";
31
31
 
32
32
  export type PublishOptions = {
33
33
  root?: string;
package/src/publish.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import pc from "picocolors";
2
2
  import { error } from "./logger";
3
- import { PublishProblem } from "./publish/protocol";
3
+ import { PublishProblem } from "@valbuild/shared/internal";
4
4
  import { formatBytes, runPublish } from "./publish/runPublish";
5
5
 
6
6
  /**
@@ -1,210 +0,0 @@
1
- /**
2
- * The publish API's types, COPIED from the service that serves them.
3
- *
4
- * Source: `content/src/handlers/Api.ts` in valbuild/home, branch
5
- * `claude/new-project-studio-saves-0g9ksb`, commit `ecf3b8d`. The routes below
6
- * are that file's `/publish*` entries and the two types they use, verbatim,
7
- * comments included, so that the two can be diffed by eye.
8
- *
9
- * **Copied rather than imported, because it cannot be imported.** That file
10
- * lives in a private repository which publishes nothing to npm, and its own
11
- * header says this is how it is kept in step: "We have also used it (by just
12
- * copying it in and setting the types there) in the @valbuild/server package to
13
- * check that we are more or less in sync."
14
- *
15
- * **When home's `Api.ts` changes, change this with it.** Everything in
16
- * `protocol.ts` is checked against these types, so a copy brought up to date
17
- * fails to compile wherever this CLI has not caught up. That is the whole
18
- * value: without it a wire change is a runtime 500 in somebody's CI, which is
19
- * how `home` and `@valbuild/server` have already diverged three times - see
20
- * `homeWireContract.test.ts` in `@valbuild/server` for the last one, and
21
- * `publishWireContract.test.ts` here for the fixtures that go with these types.
22
- *
23
- * A copy is not a guarantee, only a tripwire: nothing checks it against the
24
- * service, and the parsers in `protocol.ts` are what actually holds at runtime.
25
- */
26
-
27
- export type ContentPublishApi = {
28
- /**
29
- * Publishing a build, as a resource with a lifecycle.
30
- *
31
- * ```
32
- * POST /publish declare -> an upload slot per MISSING artifact
33
- * PUT <presigned url> upload -> each artifact, straight to storage
34
- * POST /publish/{id}/artifacts confirm -> the uploads are checked
35
- * POST /publish/{id}/verify render -> a canary build, server side
36
- * POST /publish/{id}/promote go live -> the pointer moves
37
- * GET /publish/{id} status
38
- * ```
39
- *
40
- * Four steps rather than one `POST /publish`, because they fail differently
41
- * and one call cannot say "the bytes are fine but it did not render" -- which
42
- * is the sentence a publisher most needs. `promote` is separate from `verify`
43
- * so that a dry run is the absence of a call rather than a flag.
44
- *
45
- * Not keyed by a project, like `/publish-target` above and for the same
46
- * reason: a project token names one. That is what lets a generated repository
47
- * hold one secret and no variables at all.
48
- *
49
- * ## What is deliberately not here
50
- *
51
- * No loader URL, no `vendorRev`, no `x-platform-project`. This service holds
52
- * the operator relationship with the build platform and calls it; a publisher
53
- * talks to this API and nothing else. The one exception is the presigned
54
- * upload URL, which points at object storage -- bytes do not travel through
55
- * here.
56
- *
57
- * It is also what makes this publishable by a project token at all. The
58
- * platform's own verify step publishes a canary to a throwaway project, and a
59
- * throwaway has no secrets, so the loader has nothing to check a caller
60
- * against and refuses with a 503 naming a project nobody has heard of. Behind
61
- * this API the caller is never involved in that exchange.
62
- */
63
- "/publish": {
64
- POST: {
65
- body: {
66
- /** The build's own hash. Repeating it returns the same publish. */
67
- buildHash: string;
68
- /** Null only for a seed publish. See `publishPlan.ts`. */
69
- commit: string | null;
70
- /** The branch content saves commit to. Required when `commit` is set. */
71
- branch: string | null;
72
- /** Which dependency layer this was built against, sent or not. */
73
- layerRev: string | null;
74
- /**
75
- * Whether the app links its own CSS.
76
- *
77
- * Build metadata the loader needs and no artifact carries, so it has to
78
- * be declared. Null is a third answer -- "this build did not say" --
79
- * and is not false.
80
- */
81
- linksOwnCss: boolean | null;
82
- artifacts: {
83
- /** See `publishPlan.ts` for the namespace. */
84
- key: string;
85
- sha256: string;
86
- bytes: number;
87
- }[];
88
- };
89
- res: {
90
- publishId: string;
91
- state: PublishState;
92
- project: {
93
- publicProjectId: string;
94
- /** Null when the project has no site yet -- reported, not refused. */
95
- siteUrl: string | null;
96
- };
97
- /**
98
- * One per artifact this project does not already hold, and nothing else.
99
- *
100
- * So this doubles as the answer to "what is missing": there is no
101
- * separate field to keep in step with it. Slots expire; a presigned PUT
102
- * that answers 403 means declare again, not that the publish failed.
103
- */
104
- uploads: {
105
- key: string;
106
- url: string;
107
- method: "PUT";
108
- headers: Record<string, string>;
109
- /** ISO 8601. */
110
- expiresAt: string;
111
- }[];
112
- /** Keys already held, so a caller can see what it did not have to send. */
113
- have: string[];
114
- };
115
- };
116
- };
117
- "/publish/:publishId": {
118
- GET: {
119
- res: {
120
- publishId: string;
121
- state: PublishState;
122
- buildHash: string;
123
- /** Artifact keys still not uploaded. */
124
- missing: string[];
125
- problems: PublishProblem[];
126
- };
127
- };
128
- };
129
- /** Everything asked for has been uploaded. The uploads are checked here. */
130
- "/publish/:publishId/artifacts": {
131
- POST: {
132
- res: {
133
- state: PublishState;
134
- problems: PublishProblem[];
135
- };
136
- };
137
- };
138
- /** A canary build and render, on the build platform, with our credential. */
139
- "/publish/:publishId/verify": {
140
- POST: {
141
- res: {
142
- state: PublishState;
143
- ok: boolean;
144
- /** Where the canary can be looked at, when it rendered. */
145
- previewUrl: string | null;
146
- problems: PublishProblem[];
147
- };
148
- };
149
- };
150
- /**
151
- * Move the project's pointer to this build.
152
- *
153
- * Refused when the commit is no longer the branch head -- which this service
154
- * is the authority on (see `getGitHead`), so the rule is enforced where the
155
- * data already is rather than a round trip away.
156
- */
157
- "/publish/:publishId/promote": {
158
- POST: {
159
- res: {
160
- state: PublishState;
161
- url: string | null;
162
- commit: string | null;
163
- };
164
- };
165
- };
166
- /** Exchange a personal access token for a short-lived publish token. */
167
- "/publish-token": {
168
- POST: {
169
- res: {
170
- /** Shown once, here. Nothing can produce it again. */
171
- token: string;
172
- /** ISO 8601, because a `Date` does not survive JSON as one. */
173
- expiresAt: string | null;
174
- publicProjectId: string;
175
- productionUrl: string | null;
176
- };
177
- };
178
- };
179
- };
180
-
181
- /**
182
- * Where a publish is. Mirrors `PublishState` in `utils/publishPlan.ts`, which
183
- * owns the transition table; this is the wire spelling of it.
184
- */
185
- export type PublishState =
186
- | "awaiting-artifacts"
187
- | "ready"
188
- | "verified"
189
- | "live"
190
- | "failed"
191
- | "expired";
192
-
193
- /**
194
- * Why a publish is not going anywhere.
195
- *
196
- * `code` is the stable half -- a pipeline gates on it -- and carries both this
197
- * API's own codes (`ARTIFACT_MISMATCH`, `POINTER_STALE`, the declaration codes
198
- * in `publishPlan.ts`) and the build platform's `PLATFORM*` codes passed
199
- * through from a verify, because rewording those would lose the only sentence
200
- * that says what to change.
201
- *
202
- * `hint` is not decoration. A gate that merely fails is useless in somebody
203
- * else's pipeline: they get a red build and no idea why.
204
- */
205
- export type PublishProblem = {
206
- code: string;
207
- message: string;
208
- hint?: string;
209
- keys?: string[];
210
- };
@@ -1,174 +0,0 @@
1
- import { z } from "zod";
2
- import { fromError } from "zod-validation-error";
3
- import { ContentPublishApi, PublishProblem, PublishState } from "./contentApi";
4
-
5
- /**
6
- * Reading what content answers.
7
- *
8
- * Every type here comes from `contentApi.ts`, which is a copy of the
9
- * service's own `Api.ts` - so the shapes are not re-derived from what this end
10
- * wishes it received, and a copy brought up to date fails to compile wherever
11
- * this CLI has not caught up.
12
- *
13
- * The schemas are the runtime half, and they are needed as well as the types:
14
- * what arrives is JSON over HTTP from a service that versions separately, so a
15
- * type alone asserts a shape rather than checking one. Each schema is
16
- * annotated with the copied type it must produce (`z.ZodType<DeclareResponse>`
17
- * and so on), which is what ties the two halves together - a schema that drifts
18
- * from the copy does not compile, and an answer that drifts from the schema
19
- * does not parse. `ValOpsHttp` in `@valbuild/server` reads every other content
20
- * route the same way.
21
- *
22
- * Nothing here is laxer than the copy. An optional field is optional because
23
- * the service says so; making a required one optional here would hide exactly
24
- * the drift this file exists to catch.
25
- */
26
-
27
- export type { PublishProblem, PublishState };
28
-
29
- export type DeclareBody = ContentPublishApi["/publish"]["POST"]["body"];
30
- export type DeclareResponse = ContentPublishApi["/publish"]["POST"]["res"];
31
- export type ArtifactsResponse =
32
- ContentPublishApi["/publish/:publishId/artifacts"]["POST"]["res"];
33
- export type VerifyResponse =
34
- ContentPublishApi["/publish/:publishId/verify"]["POST"]["res"];
35
- export type PromoteResponse =
36
- ContentPublishApi["/publish/:publishId/promote"]["POST"]["res"];
37
- export type StatusResponse =
38
- ContentPublishApi["/publish/:publishId"]["GET"]["res"];
39
- export type PublishTokenResponse =
40
- ContentPublishApi["/publish-token"]["POST"]["res"];
41
-
42
- /** An artifact, as it is declared: by key, by hash, by size. */
43
- export type DeclaredArtifact = DeclareBody["artifacts"][number];
44
- /** Permission to write one artifact's bytes to object storage. */
45
- export type UploadSlot = DeclareResponse["uploads"][number];
46
-
47
- const publishState: z.ZodType<PublishState> = z.enum([
48
- "awaiting-artifacts",
49
- "ready",
50
- "verified",
51
- "live",
52
- "failed",
53
- "expired",
54
- ]);
55
-
56
- const publishProblem: z.ZodType<PublishProblem> = z.object({
57
- code: z.string(),
58
- message: z.string(),
59
- hint: z.string().optional(),
60
- keys: z.array(z.string()).optional(),
61
- });
62
-
63
- const uploadSlot: z.ZodType<UploadSlot> = z.object({
64
- key: z.string(),
65
- url: z.string(),
66
- method: z.literal("PUT"),
67
- headers: z.record(z.string(), z.string()),
68
- expiresAt: z.string(),
69
- });
70
-
71
- const declareResponse: z.ZodType<DeclareResponse> = z.object({
72
- publishId: z.string(),
73
- state: publishState,
74
- project: z.object({
75
- publicProjectId: z.string(),
76
- siteUrl: z.string().nullable(),
77
- }),
78
- uploads: z.array(uploadSlot),
79
- have: z.array(z.string()),
80
- });
81
-
82
- const artifactsResponse: z.ZodType<ArtifactsResponse> = z.object({
83
- state: publishState,
84
- problems: z.array(publishProblem),
85
- });
86
-
87
- const verifyResponse: z.ZodType<VerifyResponse> = z.object({
88
- state: publishState,
89
- ok: z.boolean(),
90
- previewUrl: z.string().nullable(),
91
- problems: z.array(publishProblem),
92
- });
93
-
94
- const promoteResponse: z.ZodType<PromoteResponse> = z.object({
95
- state: publishState,
96
- url: z.string().nullable(),
97
- commit: z.string().nullable(),
98
- });
99
-
100
- const statusResponse: z.ZodType<StatusResponse> = z.object({
101
- publishId: z.string(),
102
- state: publishState,
103
- buildHash: z.string(),
104
- missing: z.array(z.string()),
105
- problems: z.array(publishProblem),
106
- });
107
-
108
- const publishTokenResponse: z.ZodType<PublishTokenResponse> = z.object({
109
- token: z.string(),
110
- expiresAt: z.string().nullable(),
111
- publicProjectId: z.string(),
112
- productionUrl: z.string().nullable(),
113
- });
114
-
115
- /**
116
- * Content answered, and the answer was not one this CLI can act on.
117
- *
118
- * Separate from a refusal, which is content saying no in a sentence meant for
119
- * a person. This is two programs disagreeing - the copy in `contentApi.ts` and
120
- * the service that has moved on from it - and the two want opposite things
121
- * said about them.
122
- */
123
- export class PublishProtocolError extends Error {
124
- constructor(message: string) {
125
- super(message);
126
- this.name = "PublishProtocolError";
127
- }
128
- }
129
-
130
- export const parseDeclare = (body: unknown): DeclareResponse =>
131
- parse(declareResponse, body, "POST /v1/publish");
132
-
133
- export const parseArtifacts = (body: unknown): ArtifactsResponse =>
134
- parse(artifactsResponse, body, "POST /v1/publish/{id}/artifacts");
135
-
136
- export const parseVerify = (body: unknown): VerifyResponse =>
137
- parse(verifyResponse, body, "POST /v1/publish/{id}/verify");
138
-
139
- export const parsePromote = (body: unknown): PromoteResponse =>
140
- parse(promoteResponse, body, "POST /v1/publish/{id}/promote");
141
-
142
- export const parseStatus = (body: unknown): StatusResponse =>
143
- parse(statusResponse, body, "GET /v1/publish/{id}");
144
-
145
- export const parsePublishToken = (
146
- body: unknown,
147
- call: string,
148
- ): PublishTokenResponse => parse(publishTokenResponse, body, call);
149
-
150
- /**
151
- * The `details` of a refusal, when it carries a list of problems.
152
- *
153
- * Every non-2xx is `{ statusCode, message, details? }`, and the publish routes
154
- * put `PublishProblem[]` in `details` - a declaration is answered with EVERY
155
- * problem rather than the first, because finding them one round trip at a time
156
- * is how a publish takes six attempts. Anything else in there is somebody
157
- * else's `details` and is left to the caller's own message.
158
- */
159
- export function parseProblems(details: unknown): PublishProblem[] {
160
- const parsed = z.array(publishProblem).safeParse(details);
161
- return parsed.success ? parsed.data : [];
162
- }
163
-
164
- function parse<T>(schema: z.ZodType<T>, body: unknown, call: string): T {
165
- const parsed = schema.safeParse(body);
166
- if (!parsed.success) {
167
- throw new PublishProtocolError(
168
- `${call} answered with something this CLI cannot read: ${fromError(
169
- parsed.error,
170
- ).toString()}`,
171
- );
172
- }
173
- return parsed.data;
174
- }