vela 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +108 -11
- package/dist/bin.js.map +3 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -20,7 +20,7 @@ import dotenv2 from "dotenv";
|
|
|
20
20
|
// package.json
|
|
21
21
|
var package_default = {
|
|
22
22
|
name: "vela",
|
|
23
|
-
version: "0.
|
|
23
|
+
version: "0.24.0",
|
|
24
24
|
type: "module",
|
|
25
25
|
description: "A CLI for creating and updating SvelteKit projects",
|
|
26
26
|
license: "MIT",
|
|
@@ -894,7 +894,7 @@ var entrySchema = v2.object({
|
|
|
894
894
|
),
|
|
895
895
|
/** The schema.org type its JSON-LD describes (`ProfessionalService`, `Restaurant`, …). */
|
|
896
896
|
schemaType: v2.optional(v2.string()),
|
|
897
|
-
/** Tarball location, relative to the index URL. */
|
|
897
|
+
/** Tarball location, relative to the index URL; a paid entry's names velastack.dev's download, which `resolveDownloadUrl` derives itself. */
|
|
898
898
|
file: v2.string(),
|
|
899
899
|
sha256: v2.pipe(v2.string(), v2.regex(/^[0-9a-f]{64}$/)),
|
|
900
900
|
size: v2.optional(v2.number()),
|
|
@@ -957,10 +957,11 @@ async function readBytes(url, options) {
|
|
|
957
957
|
signal: AbortSignal.timeout(options.timeoutMs)
|
|
958
958
|
});
|
|
959
959
|
if (!res.ok) {
|
|
960
|
-
throw new
|
|
960
|
+
throw new DownloadRefused(res.status, res.statusText, await res.text().catch(() => ""));
|
|
961
961
|
}
|
|
962
962
|
return new Uint8Array(await res.arrayBuffer());
|
|
963
963
|
}
|
|
964
|
+
var headersObject = (headers) => headers ? Object.fromEntries(new Headers(headers)) : {};
|
|
964
965
|
async function fetchTemplateIndex(url, options = {}) {
|
|
965
966
|
const now = options.now ?? Date.now();
|
|
966
967
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
@@ -981,9 +982,47 @@ async function fetchTemplateIndex(url, options = {}) {
|
|
|
981
982
|
return { index: null, stale: false, reason };
|
|
982
983
|
}
|
|
983
984
|
}
|
|
984
|
-
|
|
985
|
+
var isPaidEntry = (entry) => (entry.price ?? 0) > 0;
|
|
986
|
+
function resolveDownloadUrl(entry, indexUrl, apiUrl = API_URL) {
|
|
987
|
+
if (isPaidEntry(entry)) return `${apiUrl}/v1/templates/${entry.name}/download`;
|
|
985
988
|
return new URL(entry.file, indexUrl).href;
|
|
986
989
|
}
|
|
990
|
+
var DownloadRefused = class extends Error {
|
|
991
|
+
status;
|
|
992
|
+
body;
|
|
993
|
+
constructor(status3, statusText, body) {
|
|
994
|
+
super(`${status3} ${statusText}`.trim());
|
|
995
|
+
this.status = status3;
|
|
996
|
+
this.body = body;
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
var TemplateNotPurchased = class extends Error {
|
|
1000
|
+
constructor(entry, url) {
|
|
1001
|
+
super(
|
|
1002
|
+
`${entry.title ?? entry.name} is a paid template (US$${entry.price}). Buy it for your team at ${url}, then run this again.`
|
|
1003
|
+
);
|
|
1004
|
+
this.entry = entry;
|
|
1005
|
+
this.url = url;
|
|
1006
|
+
}
|
|
1007
|
+
entry;
|
|
1008
|
+
url;
|
|
1009
|
+
name = "TemplateNotPurchased";
|
|
1010
|
+
};
|
|
1011
|
+
function paidDownloadError(entry, error) {
|
|
1012
|
+
if (error.status === 402) {
|
|
1013
|
+
let url = `https://velabase.dev/templates/${entry.name}`;
|
|
1014
|
+
try {
|
|
1015
|
+
const parsed = JSON.parse(error.body);
|
|
1016
|
+
if (typeof parsed.url === "string") url = parsed.url;
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
return new TemplateNotPurchased(entry, url);
|
|
1020
|
+
}
|
|
1021
|
+
if (error.status === 401) {
|
|
1022
|
+
return new Error(`velastack.dev did not accept your login. Run \`vela login\` and try again.`);
|
|
1023
|
+
}
|
|
1024
|
+
return new Error(`Could not download template ${entry.name}: ${error.message}`);
|
|
1025
|
+
}
|
|
987
1026
|
function sha256(bytes) {
|
|
988
1027
|
return createHash("sha256").update(bytes).digest("hex");
|
|
989
1028
|
}
|
|
@@ -1003,11 +1042,19 @@ async function downloadTemplate(entry, indexUrl, options = {}) {
|
|
|
1003
1042
|
haveTarball = sha256(cached2) === entry.sha256;
|
|
1004
1043
|
}
|
|
1005
1044
|
if (!haveTarball) {
|
|
1006
|
-
const
|
|
1045
|
+
const paid = isPaidEntry(entry);
|
|
1046
|
+
if (paid && !options.apiKey) {
|
|
1047
|
+
throw new Error(
|
|
1048
|
+
`${entry.title ?? entry.name} is a paid template. Run \`vela login\` (or set VELA_API_KEY) so velastack.dev can check it was bought for you.`
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
const url = resolveDownloadUrl(entry, indexUrl, options.apiUrl);
|
|
1052
|
+
const headers = paid ? { ...headersObject(options.headers), Authorization: `Bearer ${options.apiKey}` } : options.headers;
|
|
1007
1053
|
let fetched;
|
|
1008
1054
|
try {
|
|
1009
|
-
fetched = await readBytes(url, { timeoutMs, headers
|
|
1055
|
+
fetched = await readBytes(url, { timeoutMs, headers });
|
|
1010
1056
|
} catch (e) {
|
|
1057
|
+
if (paid && e instanceof DownloadRefused) throw paidDownloadError(entry, e);
|
|
1011
1058
|
throw new Error(`Could not download template ${entry.name}: ${e.message}`);
|
|
1012
1059
|
}
|
|
1013
1060
|
const digest = sha256(fetched);
|
|
@@ -1115,13 +1162,13 @@ function templateChoicesMessage(templates) {
|
|
|
1115
1162
|
}
|
|
1116
1163
|
return [...groups].map(([category, names]) => `${category}: ${names.join(", ")}`).join("; ");
|
|
1117
1164
|
}
|
|
1118
|
-
async function resolveTemplate(template) {
|
|
1165
|
+
async function resolveTemplate(template, options = {}) {
|
|
1119
1166
|
if (!("entry" in template)) {
|
|
1120
1167
|
return { ...template, cleanup() {
|
|
1121
1168
|
} };
|
|
1122
1169
|
}
|
|
1123
1170
|
const { entry, indexUrl, ...info } = template;
|
|
1124
|
-
const dir = await downloadTemplate(entry, indexUrl);
|
|
1171
|
+
const dir = await downloadTemplate(entry, indexUrl, { apiKey: options.apiKey });
|
|
1125
1172
|
return {
|
|
1126
1173
|
...info,
|
|
1127
1174
|
dir,
|
|
@@ -3745,6 +3792,12 @@ async function createProject(apiKey, args) {
|
|
|
3745
3792
|
})
|
|
3746
3793
|
});
|
|
3747
3794
|
}
|
|
3795
|
+
async function purchaseTemplate(apiKey, slug2, teamId) {
|
|
3796
|
+
return apiFetch(apiKey, `/v1/templates/${slug2}/purchase`, {
|
|
3797
|
+
method: "POST",
|
|
3798
|
+
body: JSON.stringify({ team: teamId })
|
|
3799
|
+
});
|
|
3800
|
+
}
|
|
3748
3801
|
async function registerServer(apiKey, input) {
|
|
3749
3802
|
return apiFetch(apiKey, "/v1/servers", {
|
|
3750
3803
|
method: "POST",
|
|
@@ -6414,7 +6467,7 @@ async function createProject2(cwdArg, options, listing) {
|
|
|
6414
6467
|
const template = findTemplate(listing, options.template ?? DEFAULT_TEMPLATE);
|
|
6415
6468
|
checkFlagsForTemplate(template, options);
|
|
6416
6469
|
const gallerySchema = options.schema ? findGallerySchema(options.schema) : void 0;
|
|
6417
|
-
const resolved = await resolveCreatableTemplate(template);
|
|
6470
|
+
const resolved = await resolveCreatableTemplate(template, options);
|
|
6418
6471
|
const onCancel2 = () => {
|
|
6419
6472
|
resolved.cleanup();
|
|
6420
6473
|
p12.cancel("Operation cancelled.");
|
|
@@ -6476,9 +6529,28 @@ async function createProject2(cwdArg, options, listing) {
|
|
|
6476
6529
|
}
|
|
6477
6530
|
return { directory: projectPath, packageManager: packageManager2, installed: installed2, migrated, name, template, link: link2 };
|
|
6478
6531
|
}
|
|
6479
|
-
async function resolveCreatableTemplate(template) {
|
|
6532
|
+
async function resolveCreatableTemplate(template, options = {}) {
|
|
6533
|
+
let apiKey = readApiKey();
|
|
6534
|
+
if ((template.price ?? 0) > 0 && !apiKey) {
|
|
6535
|
+
if (!isInteractive()) {
|
|
6536
|
+
throw new Error(
|
|
6537
|
+
`${template.name} is a paid template. Run \`vela login\` or set VELA_API_KEY, then run this again.`
|
|
6538
|
+
);
|
|
6539
|
+
}
|
|
6540
|
+
p12.log.info(
|
|
6541
|
+
`${template.name} is a paid template (US$${template.price}). Log in to velastack.dev to download it.`
|
|
6542
|
+
);
|
|
6543
|
+
apiKey = await loginInteractively();
|
|
6544
|
+
}
|
|
6480
6545
|
if (template.source === "remote") p12.log.step(`Downloading template ${template.name}...`);
|
|
6481
|
-
|
|
6546
|
+
let resolved;
|
|
6547
|
+
try {
|
|
6548
|
+
resolved = await resolveTemplate(template, { apiKey });
|
|
6549
|
+
} catch (error) {
|
|
6550
|
+
if (!(error instanceof TemplateNotPurchased) || !apiKey || !isInteractive()) throw error;
|
|
6551
|
+
await buyTemplate(error, apiKey, options);
|
|
6552
|
+
resolved = await resolveTemplate(template, { apiKey });
|
|
6553
|
+
}
|
|
6482
6554
|
const blocker = findKit3Blocker(resolved.dir);
|
|
6483
6555
|
if (blocker) {
|
|
6484
6556
|
resolved.cleanup();
|
|
@@ -6486,6 +6558,31 @@ async function resolveCreatableTemplate(template) {
|
|
|
6486
6558
|
}
|
|
6487
6559
|
return resolved;
|
|
6488
6560
|
}
|
|
6561
|
+
async function buyTemplate(refusal, apiKey, options) {
|
|
6562
|
+
const { entry } = refusal;
|
|
6563
|
+
const title = entry.title ?? entry.name;
|
|
6564
|
+
p12.log.warn(`${title} is a paid template (US$${entry.price}).`);
|
|
6565
|
+
const team = await resolveTeam(await listTeams(apiKey), {
|
|
6566
|
+
teamId: options.team,
|
|
6567
|
+
interactive: true
|
|
6568
|
+
});
|
|
6569
|
+
const confirmed = await p12.confirm({
|
|
6570
|
+
message: `Buy ${title} for US$${entry.price} for ${team.name}? The card on file at velastack.dev is charged.`
|
|
6571
|
+
});
|
|
6572
|
+
if (p12.isCancel(confirmed) || !confirmed) throw refusal;
|
|
6573
|
+
if (options.link === void 0 || options.link === "new") options.team = team.id;
|
|
6574
|
+
const outcome = await purchaseTemplate(apiKey, entry.name, team.id);
|
|
6575
|
+
if (outcome.status === "purchased") {
|
|
6576
|
+
const card = outcome.last4 ? ` on the card ending ${outcome.last4}` : "";
|
|
6577
|
+
p12.log.success(`Bought ${title} for ${team.name}${card}. It is yours to deploy again any time.`);
|
|
6578
|
+
return;
|
|
6579
|
+
}
|
|
6580
|
+
p12.log.info(`${outcome.message}
|
|
6581
|
+
Buy it in the browser, then come back here:
|
|
6582
|
+
${outcome.url}`);
|
|
6583
|
+
const done = await p12.confirm({ message: "Bought it? The download is tried again." });
|
|
6584
|
+
if (p12.isCancel(done) || !done) throw refusal;
|
|
6585
|
+
}
|
|
6489
6586
|
async function scaffoldProject(cwdArg, options, template, resolved, onCancel2) {
|
|
6490
6587
|
const interactive = isInteractive();
|
|
6491
6588
|
let directory;
|