@minnovate/shadow-publisher 0.12.1 → 0.12.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-publisher",
|
|
3
3
|
"displayName": "Git Publisher",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.2",
|
|
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,63 @@ 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 RepositoryFileListResponse = object({
|
|
3098
|
+
commit: nullable(Oid),
|
|
3099
|
+
entries: array(
|
|
3100
|
+
object({
|
|
3101
|
+
path: FilePath,
|
|
3102
|
+
/** bytes; null for a submodule */
|
|
3103
|
+
bytes: nullable(int()),
|
|
3104
|
+
kind: _enum(["file", "symlink", "submodule"]),
|
|
3105
|
+
/** null for a symlink or a submodule */
|
|
3106
|
+
shown: nullable(FileShown)
|
|
3107
|
+
})
|
|
3108
|
+
)
|
|
3109
|
+
});
|
|
3110
|
+
var RepositoryFileResponse = object({ path: FilePath, bytes: int(), shown: FileShown, text: nullable(string2()) });
|
|
3111
|
+
var FileReadJob = object({
|
|
3112
|
+
repoId: RepoId,
|
|
3113
|
+
at: nullable(Oid),
|
|
3114
|
+
read: union([object({ kind: literal("files") }), object({ kind: literal("list") }), object({ kind: literal("file"), path: FilePath })])
|
|
3115
|
+
});
|
|
3116
|
+
var FileReadResult = union([
|
|
3117
|
+
object({ kind: literal("files"), answer: RepositoryFilesResponse }),
|
|
3118
|
+
object({ kind: literal("list"), answer: RepositoryFileListResponse }),
|
|
3119
|
+
object({ kind: literal("file"), answer: RepositoryFileResponse }),
|
|
3120
|
+
object({ kind: literal("refused"), code: _enum(ERROR_CODES), message: string2() })
|
|
3121
|
+
]);
|
|
3029
3122
|
var DownloadDescriptor = object({
|
|
3030
3123
|
url: url(),
|
|
3031
3124
|
sha256: Sha256Hex,
|
|
@@ -3867,14 +3960,14 @@ async function resolveRevs(dir, revs, opts = {}) {
|
|
|
3867
3960
|
const out = revs.map(
|
|
3868
3961
|
(r) => r.startsWith("-") ? null : r.includes("\0") ? new PublisherError("INVALID_REQUEST", "NUL in git argument") : null
|
|
3869
3962
|
);
|
|
3870
|
-
const
|
|
3871
|
-
if (
|
|
3963
|
+
const ask2 = revs.flatMap((r, i) => out[i] === null && !r.startsWith("-") ? [i] : []);
|
|
3964
|
+
if (ask2.length === 0) {
|
|
3872
3965
|
return out;
|
|
3873
3966
|
}
|
|
3874
|
-
const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname)", "-Z"], { cwd: dir, input:
|
|
3967
|
+
const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname)", "-Z"], { cwd: dir, input: ask2.map((i) => revs[i]).join("\0") + "\0", ...opts });
|
|
3875
3968
|
const records = stdout.toString("utf8").split("\0");
|
|
3876
3969
|
const misses = [];
|
|
3877
|
-
for (const [j, i] of
|
|
3970
|
+
for (const [j, i] of ask2.entries()) {
|
|
3878
3971
|
const record2 = records[j] ?? "";
|
|
3879
3972
|
if (record2 && !/ (missing|ambiguous)$/.test(record2)) {
|
|
3880
3973
|
out[i] = record2;
|
|
@@ -5043,26 +5136,27 @@ async function updateEntry(url2, change) {
|
|
|
5043
5136
|
await writeCredentials(file);
|
|
5044
5137
|
}
|
|
5045
5138
|
|
|
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
5139
|
// ../client/src/portal-request.ts
|
|
5140
|
+
var PortalSession = class {
|
|
5141
|
+
constructor(credential) {
|
|
5142
|
+
this.credential = credential;
|
|
5143
|
+
}
|
|
5144
|
+
credential;
|
|
5145
|
+
#refreshed = false;
|
|
5146
|
+
async ask(url2, body) {
|
|
5147
|
+
const { token, refresh } = this.credential;
|
|
5148
|
+
for (; ; ) {
|
|
5149
|
+
const answer2 = await askPortal(this.credential.fetch ?? fetch, url2, await token(), body);
|
|
5150
|
+
if (answer2.status === 401 && !this.#refreshed && refresh !== void 0) {
|
|
5151
|
+
this.#refreshed = true;
|
|
5152
|
+
if (await refresh()) {
|
|
5153
|
+
continue;
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
return answer2;
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
};
|
|
5066
5160
|
async function askPortal(fetchImpl, url2, token, body) {
|
|
5067
5161
|
try {
|
|
5068
5162
|
const res = await fetchImpl(url2, {
|
|
@@ -5081,10 +5175,26 @@ async function askPortal(fetchImpl, url2, token, body) {
|
|
|
5081
5175
|
return { status: 0, body: null };
|
|
5082
5176
|
}
|
|
5083
5177
|
}
|
|
5084
|
-
function
|
|
5178
|
+
function isRecord(value) {
|
|
5085
5179
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5086
5180
|
}
|
|
5087
5181
|
|
|
5182
|
+
// ../client/src/portal-refusal.ts
|
|
5183
|
+
function portalRefusalCode(body) {
|
|
5184
|
+
if (!isRecord(body)) {
|
|
5185
|
+
return null;
|
|
5186
|
+
}
|
|
5187
|
+
const error2 = body["error"];
|
|
5188
|
+
if (typeof error2 === "string") {
|
|
5189
|
+
return error2 === "" ? null : error2;
|
|
5190
|
+
}
|
|
5191
|
+
if (!isRecord(error2)) {
|
|
5192
|
+
return null;
|
|
5193
|
+
}
|
|
5194
|
+
const { code, message } = error2;
|
|
5195
|
+
return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
|
|
5196
|
+
}
|
|
5197
|
+
|
|
5088
5198
|
// ../client/src/application.ts
|
|
5089
5199
|
var APPLICATION_CREATION = "application-creation";
|
|
5090
5200
|
var ATTEMPTS = 3;
|
|
@@ -5093,19 +5203,12 @@ var REFUSALS = {
|
|
|
5093
5203
|
"repository-conflict": "REPOSITORY_CONFLICT"
|
|
5094
5204
|
};
|
|
5095
5205
|
async function createApplication(input, opts) {
|
|
5096
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5097
5206
|
const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/applications`;
|
|
5098
5207
|
const body = { creationId: input.creationId ?? newCreationId(), name: input.name, defaultBranch: input.defaultBranch ?? "main" };
|
|
5099
|
-
|
|
5208
|
+
const portal = new PortalSession(opts);
|
|
5100
5209
|
let lastOutage = "";
|
|
5101
5210
|
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
|
-
}
|
|
5211
|
+
const answer2 = await portal.ask(url2, body);
|
|
5109
5212
|
if (answer2.status === 200 || answer2.status === 201) {
|
|
5110
5213
|
return read(answer2.body, answer2.status === 201);
|
|
5111
5214
|
}
|
|
@@ -5131,9 +5234,9 @@ async function createApplication(input, opts) {
|
|
|
5131
5234
|
throw new PublisherError("UNAVAILABLE", `could not make the application: ${lastOutage}; run init again`, { retryable: true });
|
|
5132
5235
|
}
|
|
5133
5236
|
function read(body, created) {
|
|
5134
|
-
const application =
|
|
5135
|
-
const repository = RepositoryInfo.safeParse(
|
|
5136
|
-
if (!
|
|
5237
|
+
const application = isRecord(body) ? body["application"] : void 0;
|
|
5238
|
+
const repository = RepositoryInfo.safeParse(isRecord(body) ? body["repository"] : void 0);
|
|
5239
|
+
if (!isRecord(application) || typeof application["appId"] !== "string" || !repository.success) {
|
|
5137
5240
|
throw new PublisherError("UNAVAILABLE", "the portal answered with an application this version cannot read");
|
|
5138
5241
|
}
|
|
5139
5242
|
return { appId: application["appId"], created, repository: repository.data };
|
|
@@ -5271,7 +5374,7 @@ var RUN_STAGES = ["received", "running", "deploying", "live"];
|
|
|
5271
5374
|
var STAGE_WORDS = { received: "received", running: "checked", deploying: "going live", live: "live" };
|
|
5272
5375
|
var STATES = ["pending", "in-progress", "done", "failed", "skipped"];
|
|
5273
5376
|
function readStages(run2) {
|
|
5274
|
-
if (!
|
|
5377
|
+
if (!isRecord(run2)) {
|
|
5275
5378
|
return { malformed: "a run that is not an object" };
|
|
5276
5379
|
}
|
|
5277
5380
|
if (!Object.hasOwn(run2, "stages")) {
|
|
@@ -5284,7 +5387,7 @@ function readStages(run2) {
|
|
|
5284
5387
|
const out = [];
|
|
5285
5388
|
for (const [index, entry] of stages.entries()) {
|
|
5286
5389
|
const stage = RUN_STAGES[index];
|
|
5287
|
-
if (!
|
|
5390
|
+
if (!isRecord(entry) || entry["stage"] !== stage) {
|
|
5288
5391
|
return { malformed: `stage ${index + 1} is not ${stage}` };
|
|
5289
5392
|
}
|
|
5290
5393
|
const state = entry["state"];
|
|
@@ -5317,7 +5420,7 @@ function ending(stages) {
|
|
|
5317
5420
|
var POLL_INTERVAL_MS = 5e3;
|
|
5318
5421
|
var FOLLOW_TIMEOUT_MS = 15 * 6e4;
|
|
5319
5422
|
async function followRun(input, opts) {
|
|
5320
|
-
const
|
|
5423
|
+
const portal = new PortalSession(opts);
|
|
5321
5424
|
const sleep2 = opts.sleep ?? sleep;
|
|
5322
5425
|
const now = opts.now ?? Date.now;
|
|
5323
5426
|
const interval2 = opts.intervalMs ?? POLL_INTERVAL_MS;
|
|
@@ -5327,16 +5430,9 @@ async function followRun(input, opts) {
|
|
|
5327
5430
|
const seen = /* @__PURE__ */ new Map();
|
|
5328
5431
|
let last = null;
|
|
5329
5432
|
let lastError = null;
|
|
5330
|
-
let refreshed = false;
|
|
5331
5433
|
let answered = false;
|
|
5332
5434
|
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
|
-
}
|
|
5435
|
+
const answer2 = await portal.ask(url2, void 0);
|
|
5340
5436
|
if (answer2.status === 401) {
|
|
5341
5437
|
return { kind: "unauthorized" };
|
|
5342
5438
|
}
|
|
@@ -5369,12 +5465,12 @@ async function followRun(input, opts) {
|
|
|
5369
5465
|
opts.onStage?.(entry);
|
|
5370
5466
|
moved = true;
|
|
5371
5467
|
}
|
|
5372
|
-
if (
|
|
5468
|
+
if (isRecord(run2.run) && run2.run["status"] === "awaiting-approval") {
|
|
5373
5469
|
return { kind: "waiting", stages: read2.stages };
|
|
5374
5470
|
}
|
|
5375
5471
|
const end = ending(read2.stages);
|
|
5376
5472
|
if (end !== null) {
|
|
5377
|
-
const runId =
|
|
5473
|
+
const runId = isRecord(run2.run) && typeof run2.run["runId"] === "string" ? run2.run["runId"] : null;
|
|
5378
5474
|
return { kind: "settled", ending: end, failedAt: read2.stages.find((s) => s.state === "failed")?.word ?? null, stages: read2.stages, runId };
|
|
5379
5475
|
}
|
|
5380
5476
|
if (opts.untilChange === true && moved && answered) {
|
|
@@ -5393,7 +5489,7 @@ async function followRun(input, opts) {
|
|
|
5393
5489
|
}
|
|
5394
5490
|
}
|
|
5395
5491
|
function soleRun(body) {
|
|
5396
|
-
if (!
|
|
5492
|
+
if (!isRecord(body) || !Array.isArray(body["runs"])) {
|
|
5397
5493
|
return { detail: "an answer with no list of runs" };
|
|
5398
5494
|
}
|
|
5399
5495
|
const runs = body["runs"];
|
|
@@ -5402,23 +5498,6 @@ function soleRun(body) {
|
|
|
5402
5498
|
}
|
|
5403
5499
|
return { run: runs[0] ?? null };
|
|
5404
5500
|
}
|
|
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
5501
|
function portalForEndpoint(endpoint) {
|
|
5423
5502
|
let url2;
|
|
5424
5503
|
try {
|
|
@@ -5450,9 +5529,6 @@ async function confirmPortal(portal, endpoint, fetchImpl = fetch) {
|
|
|
5450
5529
|
return null;
|
|
5451
5530
|
}
|
|
5452
5531
|
}
|
|
5453
|
-
function isRecord3(value) {
|
|
5454
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5455
|
-
}
|
|
5456
5532
|
|
|
5457
5533
|
// ../client/src/preparation.ts
|
|
5458
5534
|
var PREPARATION = "preparation";
|
|
@@ -5466,38 +5542,28 @@ function answerPreparation(input, opts) {
|
|
|
5466
5542
|
return call(input, opts, { id: input.id, revision: input.revision, action: "answers", answers: input.answers });
|
|
5467
5543
|
}
|
|
5468
5544
|
async function call(input, opts, body) {
|
|
5469
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5470
5545
|
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}` };
|
|
5546
|
+
const answer2 = await new PortalSession(opts).ask(url2, body);
|
|
5547
|
+
if (answer2.status === 401) {
|
|
5548
|
+
return { kind: "unauthorized" };
|
|
5497
5549
|
}
|
|
5550
|
+
if (answer2.status === 403) {
|
|
5551
|
+
return { kind: "forbidden" };
|
|
5552
|
+
}
|
|
5553
|
+
if (answer2.status === 409) {
|
|
5554
|
+
return { kind: "moved-on" };
|
|
5555
|
+
}
|
|
5556
|
+
if (answer2.status === 404) {
|
|
5557
|
+
return portalRefusalCode(answer2.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
|
|
5558
|
+
}
|
|
5559
|
+
if (answer2.status === 200) {
|
|
5560
|
+
const view = readView(answer2.body);
|
|
5561
|
+
return "detail" in view ? { kind: "malformed", detail: view.detail } : { kind: "view", view };
|
|
5562
|
+
}
|
|
5563
|
+
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5498
5564
|
}
|
|
5499
5565
|
function readView(body) {
|
|
5500
|
-
if (!
|
|
5566
|
+
if (!isRecord(body)) {
|
|
5501
5567
|
return { detail: "an answer that is not a preparation" };
|
|
5502
5568
|
}
|
|
5503
5569
|
const { id, revision, status: status2, message, questions, publication } = body;
|
|
@@ -5518,12 +5584,12 @@ function readView(body) {
|
|
|
5518
5584
|
}
|
|
5519
5585
|
const read2 = [];
|
|
5520
5586
|
for (const q of questions) {
|
|
5521
|
-
if (!
|
|
5587
|
+
if (!isRecord(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
|
|
5522
5588
|
return { detail: "a question that cannot be answered" };
|
|
5523
5589
|
}
|
|
5524
5590
|
const options = [];
|
|
5525
5591
|
for (const o of q["options"]) {
|
|
5526
|
-
if (!
|
|
5592
|
+
if (!isRecord(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") {
|
|
5527
5593
|
return { detail: "a question with an unreadable option" };
|
|
5528
5594
|
}
|
|
5529
5595
|
options.push({ value: o["value"], label: o["label"] });
|
|
@@ -5531,7 +5597,7 @@ function readView(body) {
|
|
|
5531
5597
|
read2.push({ id: q["id"], text: q["text"], options });
|
|
5532
5598
|
}
|
|
5533
5599
|
let pub = null;
|
|
5534
|
-
if (
|
|
5600
|
+
if (isRecord(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
|
|
5535
5601
|
pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
|
|
5536
5602
|
}
|
|
5537
5603
|
return { id, revision, status: status2, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
|
|
@@ -5546,7 +5612,7 @@ function readFindings(value) {
|
|
|
5546
5612
|
if (out.length === MAX_FINDINGS) {
|
|
5547
5613
|
break;
|
|
5548
5614
|
}
|
|
5549
|
-
if (!
|
|
5615
|
+
if (!isRecord(f)) {
|
|
5550
5616
|
continue;
|
|
5551
5617
|
}
|
|
5552
5618
|
const kind = text(f["kind"]);
|
|
@@ -5572,36 +5638,26 @@ function readFindings(value) {
|
|
|
5572
5638
|
// ../client/src/creator-tools.ts
|
|
5573
5639
|
var CREATOR_TOOLS = "creator-tools";
|
|
5574
5640
|
async function runCreatorTool(input, opts) {
|
|
5575
|
-
const fetchImpl = opts.fetch ?? fetch;
|
|
5576
5641
|
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 } };
|
|
5642
|
+
const answer2 = await new PortalSession(opts).ask(url2, input.args);
|
|
5643
|
+
if (answer2.status === 401) {
|
|
5644
|
+
return { kind: "unauthorized" };
|
|
5645
|
+
}
|
|
5646
|
+
if (answer2.status === 403) {
|
|
5647
|
+
return { kind: "forbidden" };
|
|
5604
5648
|
}
|
|
5649
|
+
if (answer2.status === 404) {
|
|
5650
|
+
return { kind: "unavailable", detail: "the portal does not serve this tool" };
|
|
5651
|
+
}
|
|
5652
|
+
if (answer2.status !== 200) {
|
|
5653
|
+
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5654
|
+
}
|
|
5655
|
+
const body = answer2.body;
|
|
5656
|
+
if (!isRecord(body) || typeof body["say"] !== "string" || typeof body["next"] !== "string") {
|
|
5657
|
+
return { kind: "malformed", detail: "an answer with no say and next" };
|
|
5658
|
+
}
|
|
5659
|
+
const { say, next, isError, ...fields } = body;
|
|
5660
|
+
return { kind: "answer", answer: { say, next, isError: isError === true, fields } };
|
|
5605
5661
|
}
|
|
5606
5662
|
|
|
5607
5663
|
// ../client/src/onboard.ts
|
|
@@ -6874,6 +6930,28 @@ async function discoverEndpoint(portal, fetchImpl = fetch) {
|
|
|
6874
6930
|
return endpoint;
|
|
6875
6931
|
}
|
|
6876
6932
|
|
|
6933
|
+
// src/portal.ts
|
|
6934
|
+
var confirmed = /* @__PURE__ */ new Map();
|
|
6935
|
+
async function confirmedPortal(endpoint, opts = {}) {
|
|
6936
|
+
const derived = portalForEndpoint(endpoint);
|
|
6937
|
+
if (derived === null) {
|
|
6938
|
+
return { kind: "none" };
|
|
6939
|
+
}
|
|
6940
|
+
const asked = opts.fetch === void 0 ? confirmed.get(endpoint) ?? confirmPortal(derived, endpoint) : confirmPortal(derived, endpoint, opts.fetch);
|
|
6941
|
+
if (opts.fetch === void 0) {
|
|
6942
|
+
confirmed.set(endpoint, asked);
|
|
6943
|
+
}
|
|
6944
|
+
const config2 = await asked;
|
|
6945
|
+
return config2 === null ? { kind: "unconfirmed", portal: derived } : { kind: "confirmed", portal: derived, terminal: config2.terminal };
|
|
6946
|
+
}
|
|
6947
|
+
async function portalFor(endpoint, route, opts = {}) {
|
|
6948
|
+
const at = await confirmedPortal(endpoint, opts);
|
|
6949
|
+
if (at.kind !== "confirmed") {
|
|
6950
|
+
return { kind: "no-portal" };
|
|
6951
|
+
}
|
|
6952
|
+
return at.terminal.includes(route) ? { kind: "served", portal: at.portal } : { kind: "no-route", portal: at.portal };
|
|
6953
|
+
}
|
|
6954
|
+
|
|
6877
6955
|
// src/progress.ts
|
|
6878
6956
|
var SAYS = {
|
|
6879
6957
|
received: { doing: "Shadow is picking up your changes", done: "Shadow has your changes", still: "still picking up your changes" },
|
|
@@ -6974,22 +7052,21 @@ function stageReporter(io) {
|
|
|
6974
7052
|
|
|
6975
7053
|
// src/follow.ts
|
|
6976
7054
|
async function followPublication(p, endpoint, o, opts) {
|
|
6977
|
-
const
|
|
6978
|
-
const
|
|
6979
|
-
const portal = confirmed2 === null ? null : derived;
|
|
7055
|
+
const confirmed2 = await confirmedPortal(endpoint, opts);
|
|
7056
|
+
const portal = confirmed2.kind === "confirmed" ? confirmed2.portal : null;
|
|
6980
7057
|
const elsewhere = (reason) => ({ kind: "unavailable", reason, portal, hint: inPortal(portal, "Published.", "Follow it") });
|
|
6981
|
-
if (portal === null || confirmed2
|
|
7058
|
+
if (portal === null || confirmed2.kind !== "confirmed") {
|
|
6982
7059
|
return elsewhere("no-portal");
|
|
6983
7060
|
}
|
|
6984
7061
|
if (!confirmed2.terminal.includes(RUN_PROGRESS)) {
|
|
6985
7062
|
const noRoute = { kind: "unavailable", reason: "no-route" };
|
|
6986
7063
|
return { ...noRoute, portal, hint: progressHint(noRoute, portal) };
|
|
6987
7064
|
}
|
|
6988
|
-
const
|
|
6989
|
-
if (
|
|
7065
|
+
const moved = publishedRef(o);
|
|
7066
|
+
if (moved === null) {
|
|
6990
7067
|
return elsewhere("several-refs");
|
|
6991
7068
|
}
|
|
6992
|
-
const
|
|
7069
|
+
const { ref, commit } = moved;
|
|
6993
7070
|
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
7071
|
const outcome2 = await p.followRun(
|
|
6995
7072
|
{ portal, repoId: o.repoId, publicationId: o.publicationId, ref, commit },
|
|
@@ -7010,6 +7087,14 @@ async function followPublication(p, endpoint, o, opts) {
|
|
|
7010
7087
|
}
|
|
7011
7088
|
return { ...outcome2, portal, hint: progressHint(outcome2, portal, answerable), ...next === void 0 ? {} : { next } };
|
|
7012
7089
|
}
|
|
7090
|
+
function publishedRef(o) {
|
|
7091
|
+
const refs = Object.entries(o.refs ?? {});
|
|
7092
|
+
if (refs.length !== 1) {
|
|
7093
|
+
return null;
|
|
7094
|
+
}
|
|
7095
|
+
const [ref, commit] = refs[0];
|
|
7096
|
+
return { ref, commit };
|
|
7097
|
+
}
|
|
7013
7098
|
async function failedChecks(p, portal, repoId, outcome2, served) {
|
|
7014
7099
|
if (!served || outcome2.kind !== "settled" || outcome2.ending !== "failed" || outcome2.runId === null) {
|
|
7015
7100
|
return null;
|
|
@@ -7109,7 +7194,7 @@ function resolveChoice(answer2, names) {
|
|
|
7109
7194
|
}
|
|
7110
7195
|
return names.find((n) => n.toLowerCase() === a.toLowerCase()) ?? null;
|
|
7111
7196
|
}
|
|
7112
|
-
async function
|
|
7197
|
+
async function ask(question2) {
|
|
7113
7198
|
const rl = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stderr });
|
|
7114
7199
|
try {
|
|
7115
7200
|
return await rl.question(question2);
|
|
@@ -7127,7 +7212,7 @@ ${purpose}
|
|
|
7127
7212
|
names.forEach((n, i) => process.stderr.write(` ${i + 1}) ${n}
|
|
7128
7213
|
`));
|
|
7129
7214
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7130
|
-
const answer2 = await
|
|
7215
|
+
const answer2 = await ask(`
|
|
7131
7216
|
Which one? [1-${names.length}, or a name] `);
|
|
7132
7217
|
if (answer2 === null) {
|
|
7133
7218
|
return null;
|
|
@@ -7147,7 +7232,7 @@ Name this connection, so you never have to type ${endpoint} again.
|
|
|
7147
7232
|
(for example: prod, dev, staging)
|
|
7148
7233
|
`);
|
|
7149
7234
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7150
|
-
const answer2 = await
|
|
7235
|
+
const answer2 = await ask(suggested ? `
|
|
7151
7236
|
Profile name [${suggested}]: ` : `
|
|
7152
7237
|
Profile name: `);
|
|
7153
7238
|
if (answer2 === null) {
|
|
@@ -7580,12 +7665,12 @@ var skills = {
|
|
|
7580
7665
|
|
|
7581
7666
|
// src/init.ts
|
|
7582
7667
|
async function repositoryForInit(p, endpoint, name, opts = {}) {
|
|
7583
|
-
const
|
|
7584
|
-
if (
|
|
7668
|
+
const confirmed2 = await confirmedPortal(endpoint, opts);
|
|
7669
|
+
if (confirmed2.kind === "none") {
|
|
7585
7670
|
return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
|
|
7586
7671
|
}
|
|
7587
|
-
const
|
|
7588
|
-
if (confirmed2 ===
|
|
7672
|
+
const { portal } = confirmed2;
|
|
7673
|
+
if (confirmed2.kind === "unconfirmed") {
|
|
7589
7674
|
throw new PublisherError("UNAVAILABLE", `could not confirm that ${portal} publishes to ${endpoint}; run init again`, { retryable: true });
|
|
7590
7675
|
}
|
|
7591
7676
|
if (!confirmed2.terminal.includes(APPLICATION_CREATION)) {
|
|
@@ -7745,24 +7830,6 @@ function folderFor(name) {
|
|
|
7745
7830
|
return slug2 === "" ? "app" : slug2;
|
|
7746
7831
|
}
|
|
7747
7832
|
|
|
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
7833
|
// src/question.ts
|
|
7767
7834
|
async function questionAt(p, endpoint, repoId, opts = {}) {
|
|
7768
7835
|
const at = await portalAt(endpoint, opts);
|
|
@@ -8043,11 +8110,11 @@ async function answerSuggestion(inv, p, endpoint, repoId, o) {
|
|
|
8043
8110
|
if (id === void 0 || repoId === void 0 || o === null || o.state !== "accepted" || o.publicationId === null) {
|
|
8044
8111
|
return null;
|
|
8045
8112
|
}
|
|
8046
|
-
const
|
|
8047
|
-
|
|
8048
|
-
if (commit === void 0) {
|
|
8113
|
+
const moved = publishedRef(o);
|
|
8114
|
+
if (moved === null) {
|
|
8049
8115
|
return null;
|
|
8050
8116
|
}
|
|
8117
|
+
const { ref, commit } = moved;
|
|
8051
8118
|
return printed(
|
|
8052
8119
|
await creatorTool(p, endpoint, "resolve_suggestion", { app: repoId, suggestion: id, status: "done", publication: { publicationId: o.publicationId, ref, commit } })
|
|
8053
8120
|
);
|
|
@@ -8163,6 +8230,9 @@ var EXIT_CODE = {
|
|
|
8163
8230
|
REPOSITORY_ARCHIVED: 1,
|
|
8164
8231
|
ACCESS_CONFLICT: 1,
|
|
8165
8232
|
PUBLICATION_NOT_FOUND: 1,
|
|
8233
|
+
NOTHING_CHANGED: 1,
|
|
8234
|
+
COMMIT_NOT_FOUND: 1,
|
|
8235
|
+
PATH_NOT_FOUND: 1,
|
|
8166
8236
|
PUBLICATION_CONFLICT: 1,
|
|
8167
8237
|
PUBLICATION_EXPIRED: 1,
|
|
8168
8238
|
UPLOAD_MISSING: 1,
|