@minnovate/shadow-publisher 0.12.1 → 0.12.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.12.
|
|
4
|
+
"version": "0.12.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"]
|
package/dist/publisher.cjs
CHANGED
|
@@ -119,6 +119,12 @@ var ERROR_CODES = [
|
|
|
119
119
|
"ACCESS_CONFLICT",
|
|
120
120
|
// publications (begin / finalize / status)
|
|
121
121
|
"PUBLICATION_NOT_FOUND",
|
|
122
|
+
// A publication of files whose files are the base's already (ADR-0030).
|
|
123
|
+
"NOTHING_CHANGED",
|
|
124
|
+
// A commit the files route was asked for that the repository's refs do not reach (ADR-0030).
|
|
125
|
+
"COMMIT_NOT_FOUND",
|
|
126
|
+
// A path the files route was asked for that is not a file of that commit (ADR-0030).
|
|
127
|
+
"PATH_NOT_FOUND",
|
|
122
128
|
"PUBLICATION_CONFLICT",
|
|
123
129
|
"PUBLICATION_EXPIRED",
|
|
124
130
|
"UPLOAD_MISSING",
|
|
@@ -161,6 +167,9 @@ var HTTP_STATUS_FOR_CODE = {
|
|
|
161
167
|
REPOSITORY_ARCHIVED: 409,
|
|
162
168
|
ACCESS_CONFLICT: 412,
|
|
163
169
|
PUBLICATION_NOT_FOUND: 404,
|
|
170
|
+
NOTHING_CHANGED: 409,
|
|
171
|
+
COMMIT_NOT_FOUND: 404,
|
|
172
|
+
PATH_NOT_FOUND: 404,
|
|
164
173
|
PUBLICATION_CONFLICT: 409,
|
|
165
174
|
PUBLICATION_EXPIRED: 410,
|
|
166
175
|
UPLOAD_MISSING: 409,
|
|
@@ -2479,6 +2488,18 @@ function object(shape, params) {
|
|
|
2479
2488
|
function extend2(schema, shape) {
|
|
2480
2489
|
return extend(schema, shape);
|
|
2481
2490
|
}
|
|
2491
|
+
var ZodMiniUnion = /* @__PURE__ */ $constructor("ZodMiniUnion", (inst, def) => {
|
|
2492
|
+
$ZodUnion.init(inst, def);
|
|
2493
|
+
ZodMiniType.init(inst, def);
|
|
2494
|
+
});
|
|
2495
|
+
// @__NO_SIDE_EFFECTS__
|
|
2496
|
+
function union(options, params) {
|
|
2497
|
+
return new ZodMiniUnion({
|
|
2498
|
+
type: "union",
|
|
2499
|
+
options,
|
|
2500
|
+
...normalizeParams(params)
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2482
2503
|
var ZodMiniDiscriminatedUnion = /* @__PURE__ */ $constructor("ZodMiniDiscriminatedUnion", (inst, def) => {
|
|
2483
2504
|
$ZodDiscriminatedUnion.init(inst, def);
|
|
2484
2505
|
ZodMiniType.init(inst, def);
|
|
@@ -2666,7 +2687,14 @@ var LimitsSchema = object({
|
|
|
2666
2687
|
* Named `maxRepositoriesPerPrincipal` before 0.9.0, when it counted a creator's repositories.
|
|
2667
2688
|
* Defaulted so an older server reads as the default, not a failure.
|
|
2668
2689
|
*/
|
|
2669
|
-
maxRepositoriesPerOrganisation: _default(positiveInt(), 25)
|
|
2690
|
+
maxRepositoriesPerOrganisation: _default(positiveInt(), 25),
|
|
2691
|
+
// ---- a publication of files (ADR-0030) ----
|
|
2692
|
+
/** the UTF-8 bytes of the files one file publication writes; the commit builder is invoked with them, and Lambda takes 6 MB */
|
|
2693
|
+
maxFilePublicationBytes: _default(positiveInt(), 4 * 1024 * 1024),
|
|
2694
|
+
/** the paths one file publication writes and removes, together */
|
|
2695
|
+
maxFilePublicationPaths: _default(positiveInt(), 1e3),
|
|
2696
|
+
/** the largest file `GET …/files` shows as text, one at a time or in a listing; a larger one is `too-large` */
|
|
2697
|
+
maxShownFileBytes: _default(positiveInt(), 512 * 1024)
|
|
2670
2698
|
});
|
|
2671
2699
|
var DEFAULT_LIMITS = {
|
|
2672
2700
|
maxPackBytes: 256 * 1024 * 1024,
|
|
@@ -2684,7 +2712,10 @@ var DEFAULT_LIMITS = {
|
|
|
2684
2712
|
maxProcessMemoryBytes: 2 * 1024 * 1024 * 1024,
|
|
2685
2713
|
maxTemporaryBytes: 3 * 512 * 1024 * 1024,
|
|
2686
2714
|
gitTimeoutMs: 3e5,
|
|
2687
|
-
maxRepositoriesPerOrganisation: 25
|
|
2715
|
+
maxRepositoriesPerOrganisation: 25,
|
|
2716
|
+
maxFilePublicationBytes: 4 * 1024 * 1024,
|
|
2717
|
+
maxFilePublicationPaths: 1e3,
|
|
2718
|
+
maxShownFileBytes: 512 * 1024
|
|
2688
2719
|
};
|
|
2689
2720
|
|
|
2690
2721
|
// ../protocol/src/storage-schema.ts
|
|
@@ -2850,6 +2881,11 @@ var PublicationRequestRecord = object({
|
|
|
2850
2881
|
baseGeneration: Generation,
|
|
2851
2882
|
refUpdates: array(RefUpdate).check(_minLength(1)),
|
|
2852
2883
|
pack: nullable(PackClaim),
|
|
2884
|
+
/**
|
|
2885
|
+
* A publication of FILES (ADR-0030): the staged file set this service makes the commit from, in
|
|
2886
|
+
* the worker, in the delivery that validates it. Its one ref update names no new commit until then.
|
|
2887
|
+
*/
|
|
2888
|
+
files: _default(nullable(PackClaim), null),
|
|
2853
2889
|
client: nullable(ClientInfo),
|
|
2854
2890
|
principal: PrincipalId,
|
|
2855
2891
|
receivedAt: IsoDate,
|
|
@@ -3026,6 +3062,62 @@ var BeginPublicationResponse = object({
|
|
|
3026
3062
|
upload: nullable(UploadInstructions)
|
|
3027
3063
|
});
|
|
3028
3064
|
var PublicationStatusResponse = object({ publication: PublicationSummary });
|
|
3065
|
+
var FilePath = string2().check(_minLength(1), _maxLength(1024));
|
|
3066
|
+
var FilePublicationRequest = object({
|
|
3067
|
+
publicationId: PublicationId,
|
|
3068
|
+
/** the ref to move; the repository's head ref when left out */
|
|
3069
|
+
ref: optional(RefName),
|
|
3070
|
+
/** the commit the files go on top of: the ref's current target, or null for a ref that does not exist yet */
|
|
3071
|
+
base: nullable(Oid),
|
|
3072
|
+
/** path → the file's whole content, as UTF-8 text */
|
|
3073
|
+
files: record(FilePath, string2()),
|
|
3074
|
+
replace: _default(boolean2(), false),
|
|
3075
|
+
delete: _default(array(FilePath), []),
|
|
3076
|
+
message: string2().check(_minLength(1), _maxLength(4e3)),
|
|
3077
|
+
client: _default(nullable(ClientInfo), null)
|
|
3078
|
+
});
|
|
3079
|
+
var FilePublicationResponse = object({ publication: PublicationSummary, ref: RefName, commit: nullable(Oid) });
|
|
3080
|
+
var StagedFileSet = object({
|
|
3081
|
+
files: record(FilePath, string2()),
|
|
3082
|
+
replace: boolean2(),
|
|
3083
|
+
delete: array(FilePath),
|
|
3084
|
+
message: string2()
|
|
3085
|
+
});
|
|
3086
|
+
var RepositoryFilesResponse = object({
|
|
3087
|
+
ref: RefName,
|
|
3088
|
+
/** the ref's current target: the base a publication of files goes on */
|
|
3089
|
+
head: nullable(Oid),
|
|
3090
|
+
/** the commit these files are: `head`, or the one asked for */
|
|
3091
|
+
commit: nullable(Oid),
|
|
3092
|
+
files: record(FilePath, string2()),
|
|
3093
|
+
unreadable: array(FilePath),
|
|
3094
|
+
tooLarge: boolean2()
|
|
3095
|
+
});
|
|
3096
|
+
var FileShown = _enum(["text", "binary", "too-large"]);
|
|
3097
|
+
var ListedEntry = object({
|
|
3098
|
+
path: FilePath,
|
|
3099
|
+
/** bytes; null for a submodule */
|
|
3100
|
+
bytes: nullable(int()),
|
|
3101
|
+
kind: _enum(["file", "symlink", "submodule"]),
|
|
3102
|
+
/** null for a symlink or a submodule */
|
|
3103
|
+
shown: nullable(FileShown)
|
|
3104
|
+
});
|
|
3105
|
+
var FileListing = object({ commit: nullable(Oid), entries: array(ListedEntry) });
|
|
3106
|
+
var RepositoryFileListResponse = object({
|
|
3107
|
+
commit: nullable(Oid),
|
|
3108
|
+
entries: array(extend2(ListedEntry, { url: nullable(string2()) })),
|
|
3109
|
+
linksExpireAt: nullable(string2())
|
|
3110
|
+
});
|
|
3111
|
+
var FileReadJob = object({
|
|
3112
|
+
repoId: RepoId,
|
|
3113
|
+
at: nullable(Oid),
|
|
3114
|
+
read: union([object({ kind: literal("files") }), object({ kind: literal("list") })])
|
|
3115
|
+
});
|
|
3116
|
+
var FileReadResult = union([
|
|
3117
|
+
object({ kind: literal("files"), answer: RepositoryFilesResponse }),
|
|
3118
|
+
object({ kind: literal("list"), answer: FileListing }),
|
|
3119
|
+
object({ kind: literal("refused"), code: _enum(ERROR_CODES), message: string2() })
|
|
3120
|
+
]);
|
|
3029
3121
|
var DownloadDescriptor = object({
|
|
3030
3122
|
url: url(),
|
|
3031
3123
|
sha256: Sha256Hex,
|
|
@@ -3867,14 +3959,14 @@ async function resolveRevs(dir, revs, opts = {}) {
|
|
|
3867
3959
|
const out = revs.map(
|
|
3868
3960
|
(r) => r.startsWith("-") ? null : r.includes("\0") ? new PublisherError("INVALID_REQUEST", "NUL in git argument") : null
|
|
3869
3961
|
);
|
|
3870
|
-
const
|
|
3871
|
-
if (
|
|
3962
|
+
const ask2 = revs.flatMap((r, i) => out[i] === null && !r.startsWith("-") ? [i] : []);
|
|
3963
|
+
if (ask2.length === 0) {
|
|
3872
3964
|
return out;
|
|
3873
3965
|
}
|
|
3874
|
-
const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname)", "-Z"], { cwd: dir, input:
|
|
3966
|
+
const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname)", "-Z"], { cwd: dir, input: ask2.map((i) => revs[i]).join("\0") + "\0", ...opts });
|
|
3875
3967
|
const records = stdout.toString("utf8").split("\0");
|
|
3876
3968
|
const misses = [];
|
|
3877
|
-
for (const [j, i] of
|
|
3969
|
+
for (const [j, i] of ask2.entries()) {
|
|
3878
3970
|
const record2 = records[j] ?? "";
|
|
3879
3971
|
if (record2 && !/ (missing|ambiguous)$/.test(record2)) {
|
|
3880
3972
|
out[i] = record2;
|
|
@@ -5043,26 +5135,27 @@ async function updateEntry(url2, change) {
|
|
|
5043
5135
|
await writeCredentials(file);
|
|
5044
5136
|
}
|
|
5045
5137
|
|
|
5046
|
-
// ../client/src/portal-refusal.ts
|
|
5047
|
-
function portalRefusalCode(body) {
|
|
5048
|
-
if (!isRecord(body)) {
|
|
5049
|
-
return null;
|
|
5050
|
-
}
|
|
5051
|
-
const error2 = body["error"];
|
|
5052
|
-
if (typeof error2 === "string") {
|
|
5053
|
-
return error2 === "" ? null : error2;
|
|
5054
|
-
}
|
|
5055
|
-
if (!isRecord(error2)) {
|
|
5056
|
-
return null;
|
|
5057
|
-
}
|
|
5058
|
-
const { code, message } = error2;
|
|
5059
|
-
return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
|
|
5060
|
-
}
|
|
5061
|
-
function isRecord(value) {
|
|
5062
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5063
|
-
}
|
|
5064
|
-
|
|
5065
5138
|
// ../client/src/portal-request.ts
|
|
5139
|
+
var PortalSession = class {
|
|
5140
|
+
constructor(credential) {
|
|
5141
|
+
this.credential = credential;
|
|
5142
|
+
}
|
|
5143
|
+
credential;
|
|
5144
|
+
#refreshed = false;
|
|
5145
|
+
async ask(url2, body) {
|
|
5146
|
+
const { token, refresh } = this.credential;
|
|
5147
|
+
for (; ; ) {
|
|
5148
|
+
const answer2 = await askPortal(this.credential.fetch ?? fetch, url2, await token(), body);
|
|
5149
|
+
if (answer2.status === 401 && !this.#refreshed && refresh !== void 0) {
|
|
5150
|
+
this.#refreshed = true;
|
|
5151
|
+
if (await refresh()) {
|
|
5152
|
+
continue;
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
return answer2;
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
};
|
|
5066
5159
|
async function askPortal(fetchImpl, url2, token, body) {
|
|
5067
5160
|
try {
|
|
5068
5161
|
const res = await fetchImpl(url2, {
|
|
@@ -5081,10 +5174,26 @@ async function askPortal(fetchImpl, url2, token, body) {
|
|
|
5081
5174
|
return { status: 0, body: null };
|
|
5082
5175
|
}
|
|
5083
5176
|
}
|
|
5084
|
-
function
|
|
5177
|
+
function isRecord(value) {
|
|
5085
5178
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5086
5179
|
}
|
|
5087
5180
|
|
|
5181
|
+
// ../client/src/portal-refusal.ts
|
|
5182
|
+
function portalRefusalCode(body) {
|
|
5183
|
+
if (!isRecord(body)) {
|
|
5184
|
+
return null;
|
|
5185
|
+
}
|
|
5186
|
+
const error2 = body["error"];
|
|
5187
|
+
if (typeof error2 === "string") {
|
|
5188
|
+
return error2 === "" ? null : error2;
|
|
5189
|
+
}
|
|
5190
|
+
if (!isRecord(error2)) {
|
|
5191
|
+
return null;
|
|
5192
|
+
}
|
|
5193
|
+
const { code, message } = error2;
|
|
5194
|
+
return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
|
|
5195
|
+
}
|
|
5196
|
+
|
|
5088
5197
|
// ../client/src/application.ts
|
|
5089
5198
|
var APPLICATION_CREATION = "application-creation";
|
|
5090
5199
|
var ATTEMPTS = 3;
|
|
@@ -5093,19 +5202,12 @@ var REFUSALS = {
|
|
|
5093
5202
|
"repository-conflict": "REPOSITORY_CONFLICT"
|
|
5094
5203
|
};
|
|
5095
5204
|
async function createApplication(input, opts) {
|
|
5096
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5097
5205
|
const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/applications`;
|
|
5098
5206
|
const body = { creationId: input.creationId ?? newCreationId(), name: input.name, defaultBranch: input.defaultBranch ?? "main" };
|
|
5099
|
-
|
|
5207
|
+
const portal = new PortalSession(opts);
|
|
5100
5208
|
let lastOutage = "";
|
|
5101
5209
|
for (let attempt = 0; attempt < ATTEMPTS; ) {
|
|
5102
|
-
const answer2 = await
|
|
5103
|
-
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5104
|
-
refreshed = true;
|
|
5105
|
-
if (await opts.refresh()) {
|
|
5106
|
-
continue;
|
|
5107
|
-
}
|
|
5108
|
-
}
|
|
5210
|
+
const answer2 = await portal.ask(url2, body);
|
|
5109
5211
|
if (answer2.status === 200 || answer2.status === 201) {
|
|
5110
5212
|
return read(answer2.body, answer2.status === 201);
|
|
5111
5213
|
}
|
|
@@ -5131,9 +5233,9 @@ async function createApplication(input, opts) {
|
|
|
5131
5233
|
throw new PublisherError("UNAVAILABLE", `could not make the application: ${lastOutage}; run init again`, { retryable: true });
|
|
5132
5234
|
}
|
|
5133
5235
|
function read(body, created) {
|
|
5134
|
-
const application =
|
|
5135
|
-
const repository = RepositoryInfo.safeParse(
|
|
5136
|
-
if (!
|
|
5236
|
+
const application = isRecord(body) ? body["application"] : void 0;
|
|
5237
|
+
const repository = RepositoryInfo.safeParse(isRecord(body) ? body["repository"] : void 0);
|
|
5238
|
+
if (!isRecord(application) || typeof application["appId"] !== "string" || !repository.success) {
|
|
5137
5239
|
throw new PublisherError("UNAVAILABLE", "the portal answered with an application this version cannot read");
|
|
5138
5240
|
}
|
|
5139
5241
|
return { appId: application["appId"], created, repository: repository.data };
|
|
@@ -5271,7 +5373,7 @@ var RUN_STAGES = ["received", "running", "deploying", "live"];
|
|
|
5271
5373
|
var STAGE_WORDS = { received: "received", running: "checked", deploying: "going live", live: "live" };
|
|
5272
5374
|
var STATES = ["pending", "in-progress", "done", "failed", "skipped"];
|
|
5273
5375
|
function readStages(run2) {
|
|
5274
|
-
if (!
|
|
5376
|
+
if (!isRecord(run2)) {
|
|
5275
5377
|
return { malformed: "a run that is not an object" };
|
|
5276
5378
|
}
|
|
5277
5379
|
if (!Object.hasOwn(run2, "stages")) {
|
|
@@ -5284,7 +5386,7 @@ function readStages(run2) {
|
|
|
5284
5386
|
const out = [];
|
|
5285
5387
|
for (const [index, entry] of stages.entries()) {
|
|
5286
5388
|
const stage = RUN_STAGES[index];
|
|
5287
|
-
if (!
|
|
5389
|
+
if (!isRecord(entry) || entry["stage"] !== stage) {
|
|
5288
5390
|
return { malformed: `stage ${index + 1} is not ${stage}` };
|
|
5289
5391
|
}
|
|
5290
5392
|
const state = entry["state"];
|
|
@@ -5317,7 +5419,7 @@ function ending(stages) {
|
|
|
5317
5419
|
var POLL_INTERVAL_MS = 5e3;
|
|
5318
5420
|
var FOLLOW_TIMEOUT_MS = 15 * 6e4;
|
|
5319
5421
|
async function followRun(input, opts) {
|
|
5320
|
-
const
|
|
5422
|
+
const portal = new PortalSession(opts);
|
|
5321
5423
|
const sleep2 = opts.sleep ?? sleep;
|
|
5322
5424
|
const now = opts.now ?? Date.now;
|
|
5323
5425
|
const interval2 = opts.intervalMs ?? POLL_INTERVAL_MS;
|
|
@@ -5327,16 +5429,9 @@ async function followRun(input, opts) {
|
|
|
5327
5429
|
const seen = /* @__PURE__ */ new Map();
|
|
5328
5430
|
let last = null;
|
|
5329
5431
|
let lastError = null;
|
|
5330
|
-
let refreshed = false;
|
|
5331
5432
|
let answered = false;
|
|
5332
5433
|
for (; ; ) {
|
|
5333
|
-
const answer2 = await ask(
|
|
5334
|
-
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5335
|
-
refreshed = true;
|
|
5336
|
-
if (await opts.refresh()) {
|
|
5337
|
-
continue;
|
|
5338
|
-
}
|
|
5339
|
-
}
|
|
5434
|
+
const answer2 = await portal.ask(url2, void 0);
|
|
5340
5435
|
if (answer2.status === 401) {
|
|
5341
5436
|
return { kind: "unauthorized" };
|
|
5342
5437
|
}
|
|
@@ -5369,12 +5464,12 @@ async function followRun(input, opts) {
|
|
|
5369
5464
|
opts.onStage?.(entry);
|
|
5370
5465
|
moved = true;
|
|
5371
5466
|
}
|
|
5372
|
-
if (
|
|
5467
|
+
if (isRecord(run2.run) && run2.run["status"] === "awaiting-approval") {
|
|
5373
5468
|
return { kind: "waiting", stages: read2.stages };
|
|
5374
5469
|
}
|
|
5375
5470
|
const end = ending(read2.stages);
|
|
5376
5471
|
if (end !== null) {
|
|
5377
|
-
const runId =
|
|
5472
|
+
const runId = isRecord(run2.run) && typeof run2.run["runId"] === "string" ? run2.run["runId"] : null;
|
|
5378
5473
|
return { kind: "settled", ending: end, failedAt: read2.stages.find((s) => s.state === "failed")?.word ?? null, stages: read2.stages, runId };
|
|
5379
5474
|
}
|
|
5380
5475
|
if (opts.untilChange === true && moved && answered) {
|
|
@@ -5393,7 +5488,7 @@ async function followRun(input, opts) {
|
|
|
5393
5488
|
}
|
|
5394
5489
|
}
|
|
5395
5490
|
function soleRun(body) {
|
|
5396
|
-
if (!
|
|
5491
|
+
if (!isRecord(body) || !Array.isArray(body["runs"])) {
|
|
5397
5492
|
return { detail: "an answer with no list of runs" };
|
|
5398
5493
|
}
|
|
5399
5494
|
const runs = body["runs"];
|
|
@@ -5402,23 +5497,6 @@ function soleRun(body) {
|
|
|
5402
5497
|
}
|
|
5403
5498
|
return { run: runs[0] ?? null };
|
|
5404
5499
|
}
|
|
5405
|
-
async function ask(fetchImpl, url2, token) {
|
|
5406
|
-
try {
|
|
5407
|
-
const res = await fetchImpl(url2, { headers: { authorization: `Bearer ${token}`, accept: "application/json" }, signal: AbortSignal.timeout(3e4) });
|
|
5408
|
-
const text = await res.text();
|
|
5409
|
-
let body = null;
|
|
5410
|
-
try {
|
|
5411
|
-
body = text ? JSON.parse(text) : null;
|
|
5412
|
-
} catch {
|
|
5413
|
-
if (res.status === 200) {
|
|
5414
|
-
return { status: 200, body: void 0 };
|
|
5415
|
-
}
|
|
5416
|
-
}
|
|
5417
|
-
return { status: res.status, body };
|
|
5418
|
-
} catch {
|
|
5419
|
-
return { status: 0, body: null };
|
|
5420
|
-
}
|
|
5421
|
-
}
|
|
5422
5500
|
function portalForEndpoint(endpoint) {
|
|
5423
5501
|
let url2;
|
|
5424
5502
|
try {
|
|
@@ -5450,9 +5528,6 @@ async function confirmPortal(portal, endpoint, fetchImpl = fetch) {
|
|
|
5450
5528
|
return null;
|
|
5451
5529
|
}
|
|
5452
5530
|
}
|
|
5453
|
-
function isRecord3(value) {
|
|
5454
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5455
|
-
}
|
|
5456
5531
|
|
|
5457
5532
|
// ../client/src/preparation.ts
|
|
5458
5533
|
var PREPARATION = "preparation";
|
|
@@ -5466,38 +5541,28 @@ function answerPreparation(input, opts) {
|
|
|
5466
5541
|
return call(input, opts, { id: input.id, revision: input.revision, action: "answers", answers: input.answers });
|
|
5467
5542
|
}
|
|
5468
5543
|
async function call(input, opts, body) {
|
|
5469
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5470
5544
|
const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/repositories/${encodeURIComponent(input.repoId)}/preparation`;
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5475
|
-
refreshed = true;
|
|
5476
|
-
if (await opts.refresh()) {
|
|
5477
|
-
continue;
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
if (answer2.status === 401) {
|
|
5481
|
-
return { kind: "unauthorized" };
|
|
5482
|
-
}
|
|
5483
|
-
if (answer2.status === 403) {
|
|
5484
|
-
return { kind: "forbidden" };
|
|
5485
|
-
}
|
|
5486
|
-
if (answer2.status === 409) {
|
|
5487
|
-
return { kind: "moved-on" };
|
|
5488
|
-
}
|
|
5489
|
-
if (answer2.status === 404) {
|
|
5490
|
-
return portalRefusalCode(answer2.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
|
|
5491
|
-
}
|
|
5492
|
-
if (answer2.status === 200) {
|
|
5493
|
-
const view = readView(answer2.body);
|
|
5494
|
-
return "detail" in view ? { kind: "malformed", detail: view.detail } : { kind: "view", view };
|
|
5495
|
-
}
|
|
5496
|
-
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5545
|
+
const answer2 = await new PortalSession(opts).ask(url2, body);
|
|
5546
|
+
if (answer2.status === 401) {
|
|
5547
|
+
return { kind: "unauthorized" };
|
|
5497
5548
|
}
|
|
5549
|
+
if (answer2.status === 403) {
|
|
5550
|
+
return { kind: "forbidden" };
|
|
5551
|
+
}
|
|
5552
|
+
if (answer2.status === 409) {
|
|
5553
|
+
return { kind: "moved-on" };
|
|
5554
|
+
}
|
|
5555
|
+
if (answer2.status === 404) {
|
|
5556
|
+
return portalRefusalCode(answer2.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
|
|
5557
|
+
}
|
|
5558
|
+
if (answer2.status === 200) {
|
|
5559
|
+
const view = readView(answer2.body);
|
|
5560
|
+
return "detail" in view ? { kind: "malformed", detail: view.detail } : { kind: "view", view };
|
|
5561
|
+
}
|
|
5562
|
+
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5498
5563
|
}
|
|
5499
5564
|
function readView(body) {
|
|
5500
|
-
if (!
|
|
5565
|
+
if (!isRecord(body)) {
|
|
5501
5566
|
return { detail: "an answer that is not a preparation" };
|
|
5502
5567
|
}
|
|
5503
5568
|
const { id, revision, status: status2, message, questions, publication } = body;
|
|
@@ -5518,12 +5583,12 @@ function readView(body) {
|
|
|
5518
5583
|
}
|
|
5519
5584
|
const read2 = [];
|
|
5520
5585
|
for (const q of questions) {
|
|
5521
|
-
if (!
|
|
5586
|
+
if (!isRecord(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
|
|
5522
5587
|
return { detail: "a question that cannot be answered" };
|
|
5523
5588
|
}
|
|
5524
5589
|
const options = [];
|
|
5525
5590
|
for (const o of q["options"]) {
|
|
5526
|
-
if (!
|
|
5591
|
+
if (!isRecord(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") {
|
|
5527
5592
|
return { detail: "a question with an unreadable option" };
|
|
5528
5593
|
}
|
|
5529
5594
|
options.push({ value: o["value"], label: o["label"] });
|
|
@@ -5531,7 +5596,7 @@ function readView(body) {
|
|
|
5531
5596
|
read2.push({ id: q["id"], text: q["text"], options });
|
|
5532
5597
|
}
|
|
5533
5598
|
let pub = null;
|
|
5534
|
-
if (
|
|
5599
|
+
if (isRecord(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
|
|
5535
5600
|
pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
|
|
5536
5601
|
}
|
|
5537
5602
|
return { id, revision, status: status2, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
|
|
@@ -5546,7 +5611,7 @@ function readFindings(value) {
|
|
|
5546
5611
|
if (out.length === MAX_FINDINGS) {
|
|
5547
5612
|
break;
|
|
5548
5613
|
}
|
|
5549
|
-
if (!
|
|
5614
|
+
if (!isRecord(f)) {
|
|
5550
5615
|
continue;
|
|
5551
5616
|
}
|
|
5552
5617
|
const kind = text(f["kind"]);
|
|
@@ -5572,36 +5637,26 @@ function readFindings(value) {
|
|
|
5572
5637
|
// ../client/src/creator-tools.ts
|
|
5573
5638
|
var CREATOR_TOOLS = "creator-tools";
|
|
5574
5639
|
async function runCreatorTool(input, opts) {
|
|
5575
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5576
5640
|
const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/tools/${encodeURIComponent(input.tool)}`;
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
continue;
|
|
5584
|
-
}
|
|
5585
|
-
}
|
|
5586
|
-
if (answer2.status === 401) {
|
|
5587
|
-
return { kind: "unauthorized" };
|
|
5588
|
-
}
|
|
5589
|
-
if (answer2.status === 403) {
|
|
5590
|
-
return { kind: "forbidden" };
|
|
5591
|
-
}
|
|
5592
|
-
if (answer2.status === 404) {
|
|
5593
|
-
return { kind: "unavailable", detail: "the portal does not serve this tool" };
|
|
5594
|
-
}
|
|
5595
|
-
if (answer2.status !== 200) {
|
|
5596
|
-
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5597
|
-
}
|
|
5598
|
-
const body = answer2.body;
|
|
5599
|
-
if (!isRecord2(body) || typeof body["say"] !== "string" || typeof body["next"] !== "string") {
|
|
5600
|
-
return { kind: "malformed", detail: "an answer with no say and next" };
|
|
5601
|
-
}
|
|
5602
|
-
const { say, next, isError, ...fields } = body;
|
|
5603
|
-
return { kind: "answer", answer: { say, next, isError: isError === true, fields } };
|
|
5641
|
+
const answer2 = await new PortalSession(opts).ask(url2, input.args);
|
|
5642
|
+
if (answer2.status === 401) {
|
|
5643
|
+
return { kind: "unauthorized" };
|
|
5644
|
+
}
|
|
5645
|
+
if (answer2.status === 403) {
|
|
5646
|
+
return { kind: "forbidden" };
|
|
5604
5647
|
}
|
|
5648
|
+
if (answer2.status === 404) {
|
|
5649
|
+
return { kind: "unavailable", detail: "the portal does not serve this tool" };
|
|
5650
|
+
}
|
|
5651
|
+
if (answer2.status !== 200) {
|
|
5652
|
+
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5653
|
+
}
|
|
5654
|
+
const body = answer2.body;
|
|
5655
|
+
if (!isRecord(body) || typeof body["say"] !== "string" || typeof body["next"] !== "string") {
|
|
5656
|
+
return { kind: "malformed", detail: "an answer with no say and next" };
|
|
5657
|
+
}
|
|
5658
|
+
const { say, next, isError, ...fields } = body;
|
|
5659
|
+
return { kind: "answer", answer: { say, next, isError: isError === true, fields } };
|
|
5605
5660
|
}
|
|
5606
5661
|
|
|
5607
5662
|
// ../client/src/onboard.ts
|
|
@@ -6874,6 +6929,28 @@ async function discoverEndpoint(portal, fetchImpl = fetch) {
|
|
|
6874
6929
|
return endpoint;
|
|
6875
6930
|
}
|
|
6876
6931
|
|
|
6932
|
+
// src/portal.ts
|
|
6933
|
+
var confirmed = /* @__PURE__ */ new Map();
|
|
6934
|
+
async function confirmedPortal(endpoint, opts = {}) {
|
|
6935
|
+
const derived = portalForEndpoint(endpoint);
|
|
6936
|
+
if (derived === null) {
|
|
6937
|
+
return { kind: "none" };
|
|
6938
|
+
}
|
|
6939
|
+
const asked = opts.fetch === void 0 ? confirmed.get(endpoint) ?? confirmPortal(derived, endpoint) : confirmPortal(derived, endpoint, opts.fetch);
|
|
6940
|
+
if (opts.fetch === void 0) {
|
|
6941
|
+
confirmed.set(endpoint, asked);
|
|
6942
|
+
}
|
|
6943
|
+
const config2 = await asked;
|
|
6944
|
+
return config2 === null ? { kind: "unconfirmed", portal: derived } : { kind: "confirmed", portal: derived, terminal: config2.terminal };
|
|
6945
|
+
}
|
|
6946
|
+
async function portalFor(endpoint, route, opts = {}) {
|
|
6947
|
+
const at = await confirmedPortal(endpoint, opts);
|
|
6948
|
+
if (at.kind !== "confirmed") {
|
|
6949
|
+
return { kind: "no-portal" };
|
|
6950
|
+
}
|
|
6951
|
+
return at.terminal.includes(route) ? { kind: "served", portal: at.portal } : { kind: "no-route", portal: at.portal };
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6877
6954
|
// src/progress.ts
|
|
6878
6955
|
var SAYS = {
|
|
6879
6956
|
received: { doing: "Shadow is picking up your changes", done: "Shadow has your changes", still: "still picking up your changes" },
|
|
@@ -6974,22 +7051,21 @@ function stageReporter(io) {
|
|
|
6974
7051
|
|
|
6975
7052
|
// src/follow.ts
|
|
6976
7053
|
async function followPublication(p, endpoint, o, opts) {
|
|
6977
|
-
const
|
|
6978
|
-
const
|
|
6979
|
-
const portal = confirmed2 === null ? null : derived;
|
|
7054
|
+
const confirmed2 = await confirmedPortal(endpoint, opts);
|
|
7055
|
+
const portal = confirmed2.kind === "confirmed" ? confirmed2.portal : null;
|
|
6980
7056
|
const elsewhere = (reason) => ({ kind: "unavailable", reason, portal, hint: inPortal(portal, "Published.", "Follow it") });
|
|
6981
|
-
if (portal === null || confirmed2
|
|
7057
|
+
if (portal === null || confirmed2.kind !== "confirmed") {
|
|
6982
7058
|
return elsewhere("no-portal");
|
|
6983
7059
|
}
|
|
6984
7060
|
if (!confirmed2.terminal.includes(RUN_PROGRESS)) {
|
|
6985
7061
|
const noRoute = { kind: "unavailable", reason: "no-route" };
|
|
6986
7062
|
return { ...noRoute, portal, hint: progressHint(noRoute, portal) };
|
|
6987
7063
|
}
|
|
6988
|
-
const
|
|
6989
|
-
if (
|
|
7064
|
+
const moved = publishedRef(o);
|
|
7065
|
+
if (moved === null) {
|
|
6990
7066
|
return elsewhere("several-refs");
|
|
6991
7067
|
}
|
|
6992
|
-
const
|
|
7068
|
+
const { ref, commit } = moved;
|
|
6993
7069
|
opts.log("Following it to live: received -> checked -> going live -> live. Ctrl-C stops watching; the publication is saved and carries on without you.");
|
|
6994
7070
|
const outcome2 = await p.followRun(
|
|
6995
7071
|
{ portal, repoId: o.repoId, publicationId: o.publicationId, ref, commit },
|
|
@@ -7010,6 +7086,14 @@ async function followPublication(p, endpoint, o, opts) {
|
|
|
7010
7086
|
}
|
|
7011
7087
|
return { ...outcome2, portal, hint: progressHint(outcome2, portal, answerable), ...next === void 0 ? {} : { next } };
|
|
7012
7088
|
}
|
|
7089
|
+
function publishedRef(o) {
|
|
7090
|
+
const refs = Object.entries(o.refs ?? {});
|
|
7091
|
+
if (refs.length !== 1) {
|
|
7092
|
+
return null;
|
|
7093
|
+
}
|
|
7094
|
+
const [ref, commit] = refs[0];
|
|
7095
|
+
return { ref, commit };
|
|
7096
|
+
}
|
|
7013
7097
|
async function failedChecks(p, portal, repoId, outcome2, served) {
|
|
7014
7098
|
if (!served || outcome2.kind !== "settled" || outcome2.ending !== "failed" || outcome2.runId === null) {
|
|
7015
7099
|
return null;
|
|
@@ -7109,7 +7193,7 @@ function resolveChoice(answer2, names) {
|
|
|
7109
7193
|
}
|
|
7110
7194
|
return names.find((n) => n.toLowerCase() === a.toLowerCase()) ?? null;
|
|
7111
7195
|
}
|
|
7112
|
-
async function
|
|
7196
|
+
async function ask(question2) {
|
|
7113
7197
|
const rl = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stderr });
|
|
7114
7198
|
try {
|
|
7115
7199
|
return await rl.question(question2);
|
|
@@ -7127,7 +7211,7 @@ ${purpose}
|
|
|
7127
7211
|
names.forEach((n, i) => process.stderr.write(` ${i + 1}) ${n}
|
|
7128
7212
|
`));
|
|
7129
7213
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7130
|
-
const answer2 = await
|
|
7214
|
+
const answer2 = await ask(`
|
|
7131
7215
|
Which one? [1-${names.length}, or a name] `);
|
|
7132
7216
|
if (answer2 === null) {
|
|
7133
7217
|
return null;
|
|
@@ -7147,7 +7231,7 @@ Name this connection, so you never have to type ${endpoint} again.
|
|
|
7147
7231
|
(for example: prod, dev, staging)
|
|
7148
7232
|
`);
|
|
7149
7233
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7150
|
-
const answer2 = await
|
|
7234
|
+
const answer2 = await ask(suggested ? `
|
|
7151
7235
|
Profile name [${suggested}]: ` : `
|
|
7152
7236
|
Profile name: `);
|
|
7153
7237
|
if (answer2 === null) {
|
|
@@ -7580,12 +7664,12 @@ var skills = {
|
|
|
7580
7664
|
|
|
7581
7665
|
// src/init.ts
|
|
7582
7666
|
async function repositoryForInit(p, endpoint, name, opts = {}) {
|
|
7583
|
-
const
|
|
7584
|
-
if (
|
|
7667
|
+
const confirmed2 = await confirmedPortal(endpoint, opts);
|
|
7668
|
+
if (confirmed2.kind === "none") {
|
|
7585
7669
|
return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
|
|
7586
7670
|
}
|
|
7587
|
-
const
|
|
7588
|
-
if (confirmed2 ===
|
|
7671
|
+
const { portal } = confirmed2;
|
|
7672
|
+
if (confirmed2.kind === "unconfirmed") {
|
|
7589
7673
|
throw new PublisherError("UNAVAILABLE", `could not confirm that ${portal} publishes to ${endpoint}; run init again`, { retryable: true });
|
|
7590
7674
|
}
|
|
7591
7675
|
if (!confirmed2.terminal.includes(APPLICATION_CREATION)) {
|
|
@@ -7745,24 +7829,6 @@ function folderFor(name) {
|
|
|
7745
7829
|
return slug2 === "" ? "app" : slug2;
|
|
7746
7830
|
}
|
|
7747
7831
|
|
|
7748
|
-
// src/portal.ts
|
|
7749
|
-
var confirmed = /* @__PURE__ */ new Map();
|
|
7750
|
-
async function portalFor(endpoint, route, opts = {}) {
|
|
7751
|
-
const derived = portalForEndpoint(endpoint);
|
|
7752
|
-
if (derived === null) {
|
|
7753
|
-
return { kind: "no-portal" };
|
|
7754
|
-
}
|
|
7755
|
-
const asked = opts.fetch === void 0 ? confirmed.get(endpoint) ?? confirmPortal(derived, endpoint) : confirmPortal(derived, endpoint, opts.fetch);
|
|
7756
|
-
if (opts.fetch === void 0) {
|
|
7757
|
-
confirmed.set(endpoint, asked);
|
|
7758
|
-
}
|
|
7759
|
-
const config2 = await asked;
|
|
7760
|
-
if (config2 === null) {
|
|
7761
|
-
return { kind: "no-portal" };
|
|
7762
|
-
}
|
|
7763
|
-
return config2.terminal.includes(route) ? { kind: "served", portal: derived } : { kind: "no-route", portal: derived };
|
|
7764
|
-
}
|
|
7765
|
-
|
|
7766
7832
|
// src/question.ts
|
|
7767
7833
|
async function questionAt(p, endpoint, repoId, opts = {}) {
|
|
7768
7834
|
const at = await portalAt(endpoint, opts);
|
|
@@ -8043,11 +8109,11 @@ async function answerSuggestion(inv, p, endpoint, repoId, o) {
|
|
|
8043
8109
|
if (id === void 0 || repoId === void 0 || o === null || o.state !== "accepted" || o.publicationId === null) {
|
|
8044
8110
|
return null;
|
|
8045
8111
|
}
|
|
8046
|
-
const
|
|
8047
|
-
|
|
8048
|
-
if (commit === void 0) {
|
|
8112
|
+
const moved = publishedRef(o);
|
|
8113
|
+
if (moved === null) {
|
|
8049
8114
|
return null;
|
|
8050
8115
|
}
|
|
8116
|
+
const { ref, commit } = moved;
|
|
8051
8117
|
return printed(
|
|
8052
8118
|
await creatorTool(p, endpoint, "resolve_suggestion", { app: repoId, suggestion: id, status: "done", publication: { publicationId: o.publicationId, ref, commit } })
|
|
8053
8119
|
);
|
|
@@ -8163,6 +8229,9 @@ var EXIT_CODE = {
|
|
|
8163
8229
|
REPOSITORY_ARCHIVED: 1,
|
|
8164
8230
|
ACCESS_CONFLICT: 1,
|
|
8165
8231
|
PUBLICATION_NOT_FOUND: 1,
|
|
8232
|
+
NOTHING_CHANGED: 1,
|
|
8233
|
+
COMMIT_NOT_FOUND: 1,
|
|
8234
|
+
PATH_NOT_FOUND: 1,
|
|
8166
8235
|
PUBLICATION_CONFLICT: 1,
|
|
8167
8236
|
PUBLICATION_EXPIRED: 1,
|
|
8168
8237
|
UPLOAD_MISSING: 1,
|