@minnovate/shadow-publisher 0.11.5 → 0.11.7

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.11.5",
4
+ "version": "0.11.7",
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"]
@@ -4568,19 +4568,30 @@ var import_node_os2 = require("node:os");
4568
4568
  var import_node_path4 = require("node:path");
4569
4569
 
4570
4570
  // ../client/src/form-post.ts
4571
+ var FORM_POST_TIMEOUT_MS = 1e4;
4571
4572
  async function postForm(base, path, fields, fetchImpl = globalThis.fetch) {
4572
- let res;
4573
+ const bound = new AbortController();
4574
+ const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), FORM_POST_TIMEOUT_MS);
4573
4575
  try {
4574
- res = await fetchImpl(`${base}${path}`, {
4575
- method: "POST",
4576
- headers: { "content-type": "application/x-www-form-urlencoded" },
4577
- body: new URLSearchParams(fields).toString()
4578
- });
4579
- } catch (e) {
4580
- throw new PublisherError("UNAVAILABLE", `could not reach ${base}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true, cause: e });
4576
+ let res;
4577
+ try {
4578
+ res = await fetchImpl(`${base}${path}`, {
4579
+ method: "POST",
4580
+ headers: { "content-type": "application/x-www-form-urlencoded" },
4581
+ body: new URLSearchParams(fields).toString(),
4582
+ signal: bound.signal
4583
+ });
4584
+ } catch (e) {
4585
+ throw new PublisherError("UNAVAILABLE", `could not reach ${base}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true, cause: e });
4586
+ }
4587
+ const body = await res.json().catch(() => ({}));
4588
+ if (bound.signal.aborted) {
4589
+ throw new PublisherError("UNAVAILABLE", `could not reach ${base}: the request timed out`, { retryable: true });
4590
+ }
4591
+ return { status: res.status, body: typeof body === "object" && body !== null ? body : {} };
4592
+ } finally {
4593
+ clearTimeout(timer);
4581
4594
  }
4582
- const body = await res.json().catch(() => ({}));
4583
- return { status: res.status, body: typeof body === "object" && body !== null ? body : {} };
4584
4595
  }
4585
4596
 
4586
4597
  // ../client/src/credentials.ts
@@ -4949,6 +4960,25 @@ async function updateEntry(url2, change) {
4949
4960
  await writeCredentials(file);
4950
4961
  }
4951
4962
 
4963
+ // ../client/src/portal-refusal.ts
4964
+ function portalRefusalCode(body) {
4965
+ if (!isRecord(body)) {
4966
+ return null;
4967
+ }
4968
+ const error2 = body["error"];
4969
+ if (typeof error2 === "string") {
4970
+ return error2 === "" ? null : error2;
4971
+ }
4972
+ if (!isRecord(error2)) {
4973
+ return null;
4974
+ }
4975
+ const { code, message } = error2;
4976
+ return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
4977
+ }
4978
+ function isRecord(value) {
4979
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4980
+ }
4981
+
4952
4982
  // ../client/src/application.ts
4953
4983
  var APPLICATION_CREATION = "application-creation";
4954
4984
  var ATTEMPTS = 3;
@@ -4973,7 +5003,7 @@ async function createApplication(input, opts) {
4973
5003
  if (answer.status === 200 || answer.status === 201) {
4974
5004
  return read(answer.body, answer.status === 201);
4975
5005
  }
4976
- const code = isRecord(answer.body) && typeof answer.body["error"] === "string" ? answer.body["error"] : "";
5006
+ const code = portalRefusalCode(answer.body) ?? "";
4977
5007
  if (answer.status === 401) {
4978
5008
  throw new PublisherError("UNAUTHORIZED", 'the portal did not accept this credential; run "publisher login" again');
4979
5009
  }
@@ -4995,9 +5025,9 @@ async function createApplication(input, opts) {
4995
5025
  throw new PublisherError("UNAVAILABLE", `could not make the application: ${lastOutage}; run init again`, { retryable: true });
4996
5026
  }
4997
5027
  function read(body, created) {
4998
- const application = isRecord(body) ? body["application"] : void 0;
4999
- const repository = RepositoryInfo.safeParse(isRecord(body) ? body["repository"] : void 0);
5000
- if (!isRecord(application) || typeof application["appId"] !== "string" || !repository.success) {
5028
+ const application = isRecord2(body) ? body["application"] : void 0;
5029
+ const repository = RepositoryInfo.safeParse(isRecord2(body) ? body["repository"] : void 0);
5030
+ if (!isRecord2(application) || typeof application["appId"] !== "string" || !repository.success) {
5001
5031
  throw new PublisherError("UNAVAILABLE", "the portal answered with an application this version cannot read");
5002
5032
  }
5003
5033
  return { appId: application["appId"], created, repository: repository.data };
@@ -5020,7 +5050,7 @@ async function post(fetchImpl, url2, token, body) {
5020
5050
  return { status: 0, body: null };
5021
5051
  }
5022
5052
  }
5023
- function isRecord(value) {
5053
+ function isRecord2(value) {
5024
5054
  return typeof value === "object" && value !== null && !Array.isArray(value);
5025
5055
  }
5026
5056
 
@@ -5156,7 +5186,7 @@ var RUN_STAGES = ["received", "running", "deploying", "live"];
5156
5186
  var STAGE_WORDS = { received: "received", running: "checked", deploying: "going live", live: "live" };
5157
5187
  var STATES = ["pending", "in-progress", "done", "failed", "skipped"];
5158
5188
  function readStages(run) {
5159
- if (!isRecord2(run)) {
5189
+ if (!isRecord3(run)) {
5160
5190
  return { malformed: "a run that is not an object" };
5161
5191
  }
5162
5192
  if (!Object.hasOwn(run, "stages")) {
@@ -5169,7 +5199,7 @@ function readStages(run) {
5169
5199
  const out = [];
5170
5200
  for (const [index, entry] of stages.entries()) {
5171
5201
  const stage = RUN_STAGES[index];
5172
- if (!isRecord2(entry) || entry["stage"] !== stage) {
5202
+ if (!isRecord3(entry) || entry["stage"] !== stage) {
5173
5203
  return { malformed: `stage ${index + 1} is not ${stage}` };
5174
5204
  }
5175
5205
  const state = entry["state"];
@@ -5254,12 +5284,12 @@ async function followRun(input, opts) {
5254
5284
  opts.onStage?.(entry);
5255
5285
  moved = true;
5256
5286
  }
5257
- if (isRecord2(run.run) && run.run["status"] === "awaiting-approval") {
5287
+ if (isRecord3(run.run) && run.run["status"] === "awaiting-approval") {
5258
5288
  return { kind: "waiting", stages: read2.stages };
5259
5289
  }
5260
5290
  const end = ending(read2.stages);
5261
5291
  if (end !== null) {
5262
- const runId = isRecord2(run.run) && typeof run.run["runId"] === "string" ? run.run["runId"] : null;
5292
+ const runId = isRecord3(run.run) && typeof run.run["runId"] === "string" ? run.run["runId"] : null;
5263
5293
  return { kind: "settled", ending: end, failedAt: read2.stages.find((s) => s.state === "failed")?.word ?? null, stages: read2.stages, runId };
5264
5294
  }
5265
5295
  if (opts.untilChange === true && moved && answered) {
@@ -5278,7 +5308,7 @@ async function followRun(input, opts) {
5278
5308
  }
5279
5309
  }
5280
5310
  function soleRun(body) {
5281
- if (!isRecord2(body) || !Array.isArray(body["runs"])) {
5311
+ if (!isRecord3(body) || !Array.isArray(body["runs"])) {
5282
5312
  return { detail: "an answer with no list of runs" };
5283
5313
  }
5284
5314
  const runs = body["runs"];
@@ -5335,7 +5365,7 @@ async function confirmPortal(portal, endpoint, fetchImpl = fetch) {
5335
5365
  return null;
5336
5366
  }
5337
5367
  }
5338
- function isRecord2(value) {
5368
+ function isRecord3(value) {
5339
5369
  return typeof value === "object" && value !== null && !Array.isArray(value);
5340
5370
  }
5341
5371
 
@@ -5372,8 +5402,7 @@ async function call(input, opts, body) {
5372
5402
  return { kind: "moved-on" };
5373
5403
  }
5374
5404
  if (answer.status === 404) {
5375
- const code = isRecord3(answer.body) ? answer.body["error"] : void 0;
5376
- return code === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
5405
+ return portalRefusalCode(answer.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
5377
5406
  }
5378
5407
  if (answer.status === 200) {
5379
5408
  const view = readView(answer.body);
@@ -5383,7 +5412,7 @@ async function call(input, opts, body) {
5383
5412
  }
5384
5413
  }
5385
5414
  function readView(body) {
5386
- if (!isRecord3(body)) {
5415
+ if (!isRecord4(body)) {
5387
5416
  return { detail: "an answer that is not a preparation" };
5388
5417
  }
5389
5418
  const { id, revision, status, message, questions, publication } = body;
@@ -5404,12 +5433,12 @@ function readView(body) {
5404
5433
  }
5405
5434
  const read2 = [];
5406
5435
  for (const q of questions) {
5407
- if (!isRecord3(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
5436
+ if (!isRecord4(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
5408
5437
  return { detail: "a question that cannot be answered" };
5409
5438
  }
5410
5439
  const options = [];
5411
5440
  for (const o of q["options"]) {
5412
- if (!isRecord3(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") {
5441
+ if (!isRecord4(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") {
5413
5442
  return { detail: "a question with an unreadable option" };
5414
5443
  }
5415
5444
  options.push({ value: o["value"], label: o["label"] });
@@ -5417,7 +5446,7 @@ function readView(body) {
5417
5446
  read2.push({ id: q["id"], text: q["text"], options });
5418
5447
  }
5419
5448
  let pub = null;
5420
- if (isRecord3(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
5449
+ if (isRecord4(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
5421
5450
  pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
5422
5451
  }
5423
5452
  return { id, revision, status, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
@@ -5432,7 +5461,7 @@ function readFindings(value) {
5432
5461
  if (out.length === MAX_FINDINGS) {
5433
5462
  break;
5434
5463
  }
5435
- if (!isRecord3(f)) {
5464
+ if (!isRecord4(f)) {
5436
5465
  continue;
5437
5466
  }
5438
5467
  const kind = text(f["kind"]);
@@ -5472,7 +5501,7 @@ async function ask2(fetchImpl, url2, token, body) {
5472
5501
  return { status: 0, body: null };
5473
5502
  }
5474
5503
  }
5475
- function isRecord3(value) {
5504
+ function isRecord4(value) {
5476
5505
  return typeof value === "object" && value !== null && !Array.isArray(value);
5477
5506
  }
5478
5507
 
@@ -6464,21 +6493,28 @@ function createPublisher(opts) {
6464
6493
  }
6465
6494
 
6466
6495
  // ../client/src/migrate.ts
6467
- async function fetchCapabilities(endpoint, fetchImpl = fetch, signal) {
6468
- const res = await fetchImpl(`${normalize(endpoint)}/v1/capabilities`, { method: "GET", ...signal ? { signal } : {} });
6469
- if (!res.ok) {
6470
- throw new Error(`answered ${res.status}`);
6496
+ var CAPABILITIES_TIMEOUT_MS = 1e4;
6497
+ async function fetchCapabilities(endpoint, fetchImpl = fetch, opts = {}) {
6498
+ const bound = new AbortController();
6499
+ const timer = setTimeout(() => bound.abort(new DOMException("the capabilities request timed out", "TimeoutError")), opts.timeoutMs ?? CAPABILITIES_TIMEOUT_MS);
6500
+ const signal = opts.signal ? AbortSignal.any([opts.signal, bound.signal]) : bound.signal;
6501
+ try {
6502
+ const res = await fetchImpl(`${normalize(endpoint)}/v1/capabilities`, { method: "GET", signal });
6503
+ if (!res.ok) {
6504
+ throw new Error(`answered ${res.status}`);
6505
+ }
6506
+ return await res.json();
6507
+ } finally {
6508
+ clearTimeout(timer);
6471
6509
  }
6472
- return await res.json();
6473
6510
  }
6474
6511
  async function probeEndpoint(endpoint, opts = {}) {
6475
6512
  const from = normalize(endpoint);
6476
- const signal = AbortSignal.timeout(opts.timeoutMs ?? 1e4);
6477
6513
  let advertised;
6478
6514
  try {
6479
- advertised = (await fetchCapabilities(from, opts.fetch, signal)).endpoints?.control;
6515
+ advertised = (await fetchCapabilities(from, opts.fetch, opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {})).endpoints?.control;
6480
6516
  } catch (e) {
6481
- return { kind: "unreachable", reason: signal.aborted ? "timed out" : e.message };
6517
+ return { kind: "unreachable", reason: e.name === "TimeoutError" ? "timed out" : e.message };
6482
6518
  }
6483
6519
  if (typeof advertised !== "string" || advertised === "") {
6484
6520
  return { kind: "unreachable", reason: "it advertises no control endpoint" };
@@ -6954,11 +6990,14 @@ var DEFAULT_PORTAL_URL = "https://shadow.minnovate.ai";
6954
6990
  function portalBase(env, flag) {
6955
6991
  return (flag ?? env["GIT_PUBLISHER_PORTAL"] ?? DEFAULT_PORTAL_URL).replace(/\/+$/, "");
6956
6992
  }
6993
+ var DISCOVER_TIMEOUT_MS = 1e4;
6957
6994
  async function discoverEndpoint(portal, fetchImpl = fetch) {
6958
6995
  const url2 = `${portal}/api/v1/config`;
6996
+ const bound = new AbortController();
6997
+ const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), DISCOVER_TIMEOUT_MS);
6959
6998
  let body;
6960
6999
  try {
6961
- const response = await fetchImpl(url2);
7000
+ const response = await fetchImpl(url2, { signal: bound.signal });
6962
7001
  if (!response.ok) {
6963
7002
  throw new PublisherError("UNAVAILABLE", `could not ask ${portal} where to publish (it answered ${response.status})`, { retryable: true });
6964
7003
  }
@@ -6968,6 +7007,8 @@ async function discoverEndpoint(portal, fetchImpl = fetch) {
6968
7007
  throw e;
6969
7008
  }
6970
7009
  throw new PublisherError("NETWORK", `could not reach ${portal}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true });
7010
+ } finally {
7011
+ clearTimeout(timer);
6971
7012
  }
6972
7013
  const endpoint = body?.publishingEndpoint;
6973
7014
  if (endpoint === null || endpoint === void 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnovate/shadow-publisher",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
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",