@minnovate/shadow-publisher 0.17.2 → 0.17.3

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-publisher",
3
3
  "displayName": "Git Publisher",
4
- "version": "0.17.2",
4
+ "version": "0.17.3",
5
5
  "description": "Publish an app folder's Git history to the serverless Git publishing backend and keep it updated (\"publish my app\", \"update my app\", \"fetch the engineered version\").",
6
6
  "author": { "name": "git-publisher project" },
7
7
  "keywords": ["git", "publish", "deploy", "serverless"]
@@ -203,6 +203,8 @@ following, for when the person asks not to wait.
203
203
  moment between the merge and the publish. Run `publisher sync . --json` once more
204
204
  (`publication.next.command` says the same).
205
205
  - `REF_FORBIDDEN`: the user may not publish this branch; tell them which branch and stop.
206
+ When it comes as `error.code` with a `hint`, a limit of the organization refused it: tell them
207
+ the reason and whom to ask that the `hint` gives, and stop.
206
208
  - `MISSING_OBJECTS`: the local copy is incomplete (for example a shallow clone). Tell the user to
207
209
  publish from a complete copy: `publisher checkout <new folder> --repo <id>`.
208
210
  - `LIMIT_EXCEEDED`, `PACK_INVALID`: quote the message; suggest removing very large files from the
@@ -50,7 +50,8 @@ publisher answer <question id>=<value> --json
50
50
 
51
51
  A value that is not an option is refused before anything is sent; `error.details.questions` lists
52
52
  the choices. Exit 4 means this sign-in may not answer (an owner or admin can, or the app's creator for the
53
- first preparation after an adoption); exit 3 means ask
53
+ first preparation after an adoption); when it carries a `hint`, a limit refused it, so tell them the reason
54
+ and whom to ask that the `hint` gives. Exit 3 means ask
54
55
  again. Then `publisher follow . --publication <id> --until-change --timeout 100 --json` to see it go live, telling them each step as `publish-app` step 4 says.
55
56
 
56
57
  ## When it did not go live because of a fault on our side
@@ -226,7 +226,7 @@ var PublisherError = class _PublisherError extends Error {
226
226
  }
227
227
  };
228
228
 
229
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js
229
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
230
230
  function getEnumValues(entries) {
231
231
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
232
232
  const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
@@ -461,6 +461,18 @@ function prefixIssues(path, issues) {
461
461
  function unwrapMessage(message) {
462
462
  return typeof message === "string" ? message : message?.message;
463
463
  }
464
+ function sizeCaps(defChecks) {
465
+ const caps = [];
466
+ for (const ch of defChecks ?? []) {
467
+ const d = ch._zod.def;
468
+ if (!/_(length|size)$|_equals$/.test(d.check))
469
+ break;
470
+ const max = d.maximum ?? d.length ?? d.size;
471
+ if (max >= 0)
472
+ caps.push([max, ch]);
473
+ }
474
+ return caps;
475
+ }
464
476
  function attachSchema(issues, start2, inst) {
465
477
  var _a2;
466
478
  for (let i = start2; i < issues.length; i++) {
@@ -636,7 +648,7 @@ function installLazyProp(inst, key, make, enumerable) {
636
648
  });
637
649
  }
638
650
 
639
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/core.js
651
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/core.js
640
652
  var _a;
641
653
  var _zodDesc = { value: void 0, enumerable: false };
642
654
  var _E = "captureStackTrace" in Error ? Error : null;
@@ -748,7 +760,7 @@ function config(newConfig) {
748
760
  return globalConfig;
749
761
  }
750
762
 
751
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/errors.js
763
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/errors.js
752
764
  function _getMessage() {
753
765
  const internals = this._zod;
754
766
  internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));
@@ -793,7 +805,7 @@ var $ZodRealError = $constructor("$ZodError", initializer, void 0, {
793
805
  Parent: Error
794
806
  });
795
807
 
796
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/parse.js
808
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/parse.js
797
809
  var _parse = (_Err) => {
798
810
  const fn = (schema, value, _ctx, _params) => {
799
811
  const ctx = _ctx ? { ..._ctx, async: false } : { async: false };
@@ -864,7 +876,7 @@ var _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
864
876
  };
865
877
  var safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError);
866
878
 
867
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/regexes.js
879
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/regexes.js
868
880
  var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
869
881
  var httpProtocol = /^https?$/;
870
882
  var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
@@ -892,7 +904,7 @@ var anyString = /^[\s\S]{0,}$/;
892
904
  var number = /^-?\d+(?:\.\d+)?$/;
893
905
  var boolean = /^(?:true|false)$/i;
894
906
 
895
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/checks.js
907
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/checks.js
896
908
  var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
897
909
  var _a2;
898
910
  inst._zod ?? (inst._zod = {});
@@ -1081,14 +1093,14 @@ var $ZodCheckRegex = /* @__PURE__ */ $constructor("$ZodCheckRegex", (inst, def)
1081
1093
  };
1082
1094
  });
1083
1095
 
1084
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/versions.js
1096
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/versions.js
1085
1097
  var version = {
1086
1098
  major: 4,
1087
1099
  minor: 6,
1088
- patch: 5
1100
+ patch: 7
1089
1101
  };
1090
1102
 
1091
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/schemas.js
1103
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/schemas.js
1092
1104
  var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
1093
1105
  var _a2;
1094
1106
  inst ?? (inst = {});
@@ -1423,6 +1435,29 @@ var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
1423
1435
  $ZodType.init(inst, def);
1424
1436
  inst._zod.parse = (payload) => payload;
1425
1437
  });
1438
+ function capSize(inst, size) {
1439
+ const caps = sizeCaps(inst._zod.def.checks);
1440
+ if (!caps.length)
1441
+ return;
1442
+ const cap = Math.min(...caps.map((c) => c[0]));
1443
+ const parse3 = inst._zod.parse;
1444
+ inst._zod.parse = (payload, ctx) => {
1445
+ const n = size(payload.value);
1446
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
1447
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
1448
+ if (n > max && ch._zod.def.when(payload)) {
1449
+ const start2 = payload.issues.length;
1450
+ ch._zod.check(payload);
1451
+ payload.issues[start2].continue = false;
1452
+ attachSchema(payload.issues, start2, inst);
1453
+ return payload;
1454
+ }
1455
+ }
1456
+ }
1457
+ return parse3(payload, ctx);
1458
+ };
1459
+ }
1460
+ var arraySize = (input) => Array.isArray(input) ? input.length : -1;
1426
1461
  function handleArrayResult(result, final, index) {
1427
1462
  if (result.issues.length) {
1428
1463
  final.issues.push(...prefixIssues(index, result.issues));
@@ -1466,6 +1501,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
1466
1501
  }
1467
1502
  return payload;
1468
1503
  };
1504
+ capSize(inst, arraySize);
1469
1505
  });
1470
1506
  function handlePropertyResult(result, final, key, input, optin, optout) {
1471
1507
  const isPresent = key in input;
@@ -2098,7 +2134,7 @@ function handleRefineResult(result, payload, input, inst) {
2098
2134
  }
2099
2135
  }
2100
2136
 
2101
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/en.js
2137
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/locales/en.js
2102
2138
  var error = () => {
2103
2139
  const Sizable = {
2104
2140
  string: { unit: "characters", verb: "to have" },
@@ -2223,7 +2259,7 @@ function en_default() {
2223
2259
  };
2224
2260
  }
2225
2261
 
2226
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/api.js
2262
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/api.js
2227
2263
  function snapshotChecks(def) {
2228
2264
  if (def.checks)
2229
2265
  def.checks = [...def.checks];
@@ -2369,7 +2405,7 @@ function _refine(Class, fn, _params) {
2369
2405
  return schema;
2370
2406
  }
2371
2407
 
2372
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/mini/schemas.js
2408
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/mini/schemas.js
2373
2409
  var ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => {
2374
2410
  if (!inst._zod)
2375
2411
  throw new Error("Uninitialized schema in ZodMiniType.");
@@ -2614,7 +2650,7 @@ function refine(fn, _params = {}) {
2614
2650
  return _refine(ZodMiniCustom, fn, _params);
2615
2651
  }
2616
2652
 
2617
- // ../../../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/mini/iso.js
2653
+ // ../../../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/mini/iso.js
2618
2654
  var iso_exports = {};
2619
2655
  __export(iso_exports, {
2620
2656
  ZodMiniISODate: () => ZodMiniISODate,
@@ -5235,6 +5271,16 @@ function portalRefusalCode(body) {
5235
5271
  const { code, message } = error2;
5236
5272
  return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
5237
5273
  }
5274
+ function portalRefusalLimit(body) {
5275
+ const error2 = isRecord(body) ? body["error"] : null;
5276
+ const details = isRecord(error2) ? error2["details"] : null;
5277
+ if (!isRecord(details)) {
5278
+ return null;
5279
+ }
5280
+ const text = (value) => typeof value === "string" && value !== "" ? value : null;
5281
+ const limit = { permission: text(details["permission"]), reason: text(details["reason"]), contact: text(details["contact"]) };
5282
+ return limit.permission === null && limit.reason === null && limit.contact === null ? null : limit;
5283
+ }
5238
5284
 
5239
5285
  // ../client/src/application.ts
5240
5286
  var APPLICATION_CREATION = "application-creation";
@@ -5258,7 +5304,12 @@ async function createApplication(input, opts) {
5258
5304
  throw new PublisherError("UNAUTHORIZED", 'the portal did not accept this credential; run "publisher login" again');
5259
5305
  }
5260
5306
  if (answer2.status === 403) {
5261
- throw new PublisherError("FORBIDDEN", answer2.merged ?? `the portal refused to make an application here (${code || "forbidden"})`);
5307
+ const limit = portalRefusalLimit(answer2.body);
5308
+ throw new PublisherError(
5309
+ "FORBIDDEN",
5310
+ answer2.merged ?? `the portal refused to make an application here (${limit?.permission ?? (code || "forbidden")})`,
5311
+ limit === null ? {} : { details: limit }
5312
+ );
5262
5313
  }
5263
5314
  if (answer2.status === 409 && REFUSALS[code] !== void 0) {
5264
5315
  throw new PublisherError(REFUSALS[code], `the portal refused the repository (${code})`, { details: { portal: code } });
@@ -5491,7 +5542,7 @@ async function followRun(input, opts) {
5491
5542
  return { kind: "unavailable", reason: "no-route" };
5492
5543
  }
5493
5544
  if (answer2.status === 403) {
5494
- return { kind: "forbidden", say: answer2.merged };
5545
+ return { kind: "forbidden", say: answer2.merged, limit: portalRefusalLimit(answer2.body) };
5495
5546
  }
5496
5547
  if (answer2.status === 200) {
5497
5548
  const run2 = soleRun(answer2.body);
@@ -5601,7 +5652,7 @@ async function call(input, opts, body) {
5601
5652
  return { kind: "unauthorized" };
5602
5653
  }
5603
5654
  if (answer2.status === 403) {
5604
- return { kind: "forbidden", say: answer2.merged };
5655
+ return { kind: "forbidden", say: answer2.merged, limit: portalRefusalLimit(answer2.body) };
5605
5656
  }
5606
5657
  if (answer2.status === 409) {
5607
5658
  return { kind: "moved-on" };
@@ -5706,7 +5757,7 @@ async function runCreatorTool(input, opts) {
5706
5757
  return { kind: "unauthorized" };
5707
5758
  }
5708
5759
  if (answer2.status === 403) {
5709
- return { kind: "forbidden", say: answer2.merged };
5760
+ return { kind: "forbidden", say: answer2.merged, limit: portalRefusalLimit(answer2.body) };
5710
5761
  }
5711
5762
  if (answer2.status === 404) {
5712
5763
  return { kind: "unavailable", detail: "the portal does not serve this tool" };
@@ -6997,6 +7048,26 @@ async function discoverEndpoint(portal, fetchImpl = fetch) {
6997
7048
  return endpoint;
6998
7049
  }
6999
7050
 
7051
+ // src/limited.ts
7052
+ var REFUSED = /* @__PURE__ */ new Set(["REF_FORBIDDEN", "FORBIDDEN"]);
7053
+ var Limited = class _Limited {
7054
+ /** The lines for a refusal with `code` and `details`, or null when it is not a permission's or names no reason and no contact. */
7055
+ static hint(code, details) {
7056
+ const lines = REFUSED.has(code) ? _Limited.lines(details) : [];
7057
+ return lines.length === 0 ? null : lines.join("\n ");
7058
+ }
7059
+ /** The same words in one line, for a sentence that a 403 ends, such as the one `follow` relays; '' for none. */
7060
+ static inline(details) {
7061
+ return _Limited.lines(details).join(" ");
7062
+ }
7063
+ static lines(details) {
7064
+ const text = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
7065
+ const reason = text(details?.["reason"]);
7066
+ const contact = text(details?.["contact"]);
7067
+ return [reason === null ? null : `The organization's reason: ${reason}`, contact === null ? null : `Ask ${contact}.`].filter((line) => line !== null);
7068
+ }
7069
+ };
7070
+
7000
7071
  // src/portal.ts
7001
7072
  var confirmed = /* @__PURE__ */ new Map();
7002
7073
  async function confirmedPortal(endpoint, opts = {}) {
@@ -7235,8 +7306,11 @@ function progressHint(r, portal, answerable = false) {
7235
7306
  return inPortal(portal, "Published. This portal cannot show progress in the terminal yet.", "Follow it");
7236
7307
  case "unauthorized":
7237
7308
  return inPortal(portal, "Published, but the portal did not accept this sign-in (run publisher login).", "Follow it");
7238
- case "forbidden":
7239
- return inPortal(portal, r.say === null ? "Published, but the portal would not show this sign-in the run." : `Published. ${r.say}`, "Follow it");
7309
+ case "forbidden": {
7310
+ const limited = Limited.inline(r.limit);
7311
+ const lead = r.say === null ? "Published, but the portal would not show this sign-in the run." : `Published. ${r.say}`;
7312
+ return inPortal(portal, limited === "" ? lead : `${lead} ${limited}`, "Follow it");
7313
+ }
7240
7314
  case "malformed":
7241
7315
  return inPortal(portal, `Published, but the portal's answer about the run could not be read (${r.detail}).`, "Follow it");
7242
7316
  }
@@ -7922,7 +7996,7 @@ async function questionAt(p, endpoint, repoId, opts = {}) {
7922
7996
  if ("reason" in at) {
7923
7997
  return at;
7924
7998
  }
7925
- return shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
7999
+ return shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }), REFUSED2.read);
7926
8000
  }
7927
8001
  async function answerAt(p, endpoint, repoId, specs, opts = {}) {
7928
8002
  const answers = parseSpecs(specs);
@@ -7930,7 +8004,7 @@ async function answerAt(p, endpoint, repoId, specs, opts = {}) {
7930
8004
  if ("reason" in at) {
7931
8005
  return at;
7932
8006
  }
7933
- const read2 = shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
8007
+ const read2 = shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }), REFUSED2.read);
7934
8008
  if (read2.kind !== "view") {
7935
8009
  return read2;
7936
8010
  }
@@ -7949,7 +8023,7 @@ async function answerAt(p, endpoint, repoId, specs, opts = {}) {
7949
8023
  throw new PublisherError("INVALID_REQUEST", `"${value}" is not one of ${values.join(", ")} for "${id}"`, { details: { questions: read2.questions } });
7950
8024
  }
7951
8025
  }
7952
- return shown(at.portal, await p.answerPreparation({ portal: at.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision, answers }));
8026
+ return shown(at.portal, await p.answerPreparation({ portal: at.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision, answers }), REFUSED2.answer);
7953
8027
  }
7954
8028
  async function retryAt(p, endpoint, repoId, opts = {}) {
7955
8029
  const read2 = await questionAt(p, endpoint, repoId, opts);
@@ -7962,7 +8036,7 @@ async function retryAt(p, endpoint, repoId, opts = {}) {
7962
8036
  if (!read2.preparation.canRetry) {
7963
8037
  throw new PublisherError("LOCAL_STATE", "this version cannot be tried again: only one that stopped because of a fault on our side can, three times at most");
7964
8038
  }
7965
- const tried = shown(read2.portal, await p.retryPreparation({ portal: read2.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision }));
8039
+ const tried = shown(read2.portal, await p.retryPreparation({ portal: read2.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision }), REFUSED2.retry);
7966
8040
  return tried.kind === "view" ? { ...tried, hint: "It is being tried again. Run: publisher follow, or watch it in the portal." } : tried;
7967
8041
  }
7968
8042
  function parseSpecs(specs) {
@@ -7995,7 +8069,12 @@ async function portalAt(endpoint, opts) {
7995
8069
  };
7996
8070
  }
7997
8071
  }
7998
- function shown(portal, r) {
8072
+ var REFUSED2 = {
8073
+ read: "the portal will not show this sign-in the preparation in this organization",
8074
+ answer: "the portal will not let this sign-in answer for this organization (an owner or admin can, or the app's creator for the first preparation after an adoption)",
8075
+ retry: "the portal will not let this sign-in try it again in this organization (an owner or admin can, or the app's creator, except for the first preparation after an adoption)"
8076
+ };
8077
+ function shown(portal, r, refused) {
7999
8078
  switch (r.kind) {
8000
8079
  case "view": {
8001
8080
  const { questions, ...preparation } = r.view;
@@ -8008,10 +8087,7 @@ function shown(portal, r) {
8008
8087
  case "unauthorized":
8009
8088
  throw new PublisherError("UNAUTHORIZED", "the portal did not accept this sign-in; run publisher login");
8010
8089
  case "forbidden":
8011
- throw new PublisherError(
8012
- "FORBIDDEN",
8013
- r.say ?? "the portal will not let this sign-in answer for this organization (an owner or admin can, or the app's creator for the first preparation after an adoption)"
8014
- );
8090
+ throw new PublisherError("FORBIDDEN", r.say ?? refused, r.limit === null ? {} : { details: r.limit });
8015
8091
  case "unavailable":
8016
8092
  throw new PublisherError("UNAVAILABLE", `the portal could not be asked: ${r.detail}`, { retryable: true });
8017
8093
  case "malformed":
@@ -8094,7 +8170,11 @@ async function creatorTool(p, endpoint, tool, args, opts = {}) {
8094
8170
  case "unauthorized":
8095
8171
  throw new PublisherError("UNAUTHORIZED", "the portal did not accept this sign-in; run publisher login");
8096
8172
  case "forbidden":
8097
- throw new PublisherError("FORBIDDEN", result.say ?? "the portal will not let this sign-in do that in this organization");
8173
+ throw new PublisherError(
8174
+ "FORBIDDEN",
8175
+ result.say ?? "the portal will not let this sign-in do that in this organization",
8176
+ result.limit === null ? {} : { details: result.limit }
8177
+ );
8098
8178
  case "unavailable":
8099
8179
  throw new PublisherError("UNAVAILABLE", `the portal could not be asked: ${result.detail}`, { retryable: true });
8100
8180
  case "malformed":
@@ -8667,7 +8747,7 @@ main(process.argv.slice(2)).then(
8667
8747
  (e) => {
8668
8748
  const json = process.argv.includes("--json");
8669
8749
  if (e instanceof PublisherError) {
8670
- const moved = Moved.hint(e.code, e.details);
8750
+ const moved = Moved.hint(e.code, e.details) ?? Limited.hint(e.code, e.details);
8671
8751
  const payload = { error: { code: e.code, message: e.message, retryable: e.retryable, details: e.details ?? null }, ...moved === null ? {} : { hint: moved } };
8672
8752
  const hint = e.code === "NETWORK" ? `
8673
8753
  if this folder's service moved, "publisher doctor" says where to` : moved === null ? "" : `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnovate/shadow-publisher",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
4
4
  "description": "publisher: publish an app's Git history to the serverless Git backend",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",