@prisma/cli 2.20.0 → 3.0.0-alpha.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/LICENSE +158 -0
- package/README.md +29 -28
- package/dist/adapters/config.js +74 -0
- package/dist/adapters/local-state.js +98 -0
- package/dist/adapters/mock-api.js +57 -0
- package/dist/adapters/token-storage.js +43 -0
- package/dist/cli.js +9 -0
- package/dist/cli2.js +59 -0
- package/dist/commands/app/index.js +223 -0
- package/dist/commands/auth/index.js +42 -0
- package/dist/commands/branch/index.js +51 -0
- package/dist/commands/project/index.js +45 -0
- package/dist/controllers/app.js +813 -0
- package/dist/controllers/auth.js +107 -0
- package/dist/controllers/branch.js +73 -0
- package/dist/controllers/project.js +214 -0
- package/dist/controllers/select-prompt-port.js +12 -0
- package/dist/lib/app/bun-project.js +39 -0
- package/dist/lib/app/env-vars.js +24 -0
- package/dist/lib/app/local-dev.js +149 -0
- package/dist/lib/app/preview-build.js +283 -0
- package/dist/lib/app/preview-interaction.js +38 -0
- package/dist/lib/app/preview-progress.js +139 -0
- package/dist/lib/app/preview-provider.js +232 -0
- package/dist/lib/auth/auth-ops.js +57 -0
- package/dist/lib/auth/client.js +22 -0
- package/dist/lib/auth/guard.js +34 -0
- package/dist/lib/auth/login.js +117 -0
- package/dist/output/patterns.js +93 -0
- package/dist/presenters/app.js +405 -0
- package/dist/presenters/auth.js +73 -0
- package/dist/presenters/branch.js +111 -0
- package/dist/presenters/project.js +84 -0
- package/dist/shell/command-meta.js +320 -0
- package/dist/shell/command-runner.js +33 -0
- package/dist/shell/errors.js +66 -0
- package/dist/shell/global-flags.js +25 -0
- package/dist/shell/help.js +78 -0
- package/dist/shell/output.js +54 -0
- package/dist/shell/prompt.js +31 -0
- package/dist/shell/runtime.js +51 -0
- package/dist/shell/ui.js +59 -0
- package/dist/use-cases/auth.js +70 -0
- package/dist/use-cases/branch.js +95 -0
- package/dist/use-cases/create-cli-gateways.js +93 -0
- package/dist/use-cases/project.js +75 -0
- package/package.json +49 -137
- package/build/child.js +0 -4110
- package/build/index.js +0 -104447
- package/build/public/demo.html +0 -17
- package/build/public/demoChunk.js +0 -2
- package/build/public/electron-darwin.html +0 -19
- package/build/public/electron-linux.html +0 -18
- package/build/public/electron-mac.html +0 -18
- package/build/public/electron-win.html +0 -17
- package/build/public/electron-win32.html +0 -18
- package/build/public/electron.html +0 -17
- package/build/public/electronBus.js +0 -2
- package/build/public/electronChunk.js +0 -2
- package/build/public/favicon/apple-touch-icon.png +0 -0
- package/build/public/favicon/favicon-16x16.png +0 -0
- package/build/public/favicon/favicon-32x32.png +0 -0
- package/build/public/favicon/prisma.png +0 -0
- package/build/public/fonts/Inter.ttf +0 -0
- package/build/public/fonts/RobotoMono.ttf +0 -0
- package/build/public/icons/.DS_Store +0 -0
- package/build/public/icons/alert.svg +0 -5
- package/build/public/icons/array.svg +0 -4
- package/build/public/icons/bin.svg +0 -37
- package/build/public/icons/boolean.svg +0 -4
- package/build/public/icons/check.svg +0 -3
- package/build/public/icons/chevron-down.svg +0 -3
- package/build/public/icons/code.svg +0 -4
- package/build/public/icons/cross.svg +0 -11
- package/build/public/icons/data-tool.svg +0 -5
- package/build/public/icons/database.svg +0 -4
- package/build/public/icons/datetime.svg +0 -4
- package/build/public/icons/double-arrow-right.svg +0 -4
- package/build/public/icons/download.svg +0 -4
- package/build/public/icons/ellipsis.svg +0 -10
- package/build/public/icons/enum.svg +0 -6
- package/build/public/icons/expand.svg +0 -4
- package/build/public/icons/eye.svg +0 -20
- package/build/public/icons/filters.svg +0 -5
- package/build/public/icons/folder.svg +0 -6
- package/build/public/icons/hamburger.svg +0 -4
- package/build/public/icons/icon.svg +0 -199
- package/build/public/icons/logo.svg +0 -199
- package/build/public/icons/logotype.svg +0 -4
- package/build/public/icons/number.svg +0 -7
- package/build/public/icons/object.svg +0 -5
- package/build/public/icons/play.svg +0 -6
- package/build/public/icons/plus.svg +0 -4
- package/build/public/icons/refresh.svg +0 -4
- package/build/public/icons/search.svg +0 -7
- package/build/public/icons/settings.svg +0 -8
- package/build/public/icons/string.svg +0 -4
- package/build/public/icons/tick-indeterminate.svg +0 -3
- package/build/public/icons/tick.svg +0 -4
- package/build/public/illustrations/.DS_Store +0 -0
- package/build/public/illustrations/empty.svg +0 -1
- package/build/public/illustrations/read.svg +0 -1
- package/build/public/illustrations/searching.svg +0 -1
- package/build/public/images/.DS_Store +0 -0
- package/build/public/images/icon-1024.png +0 -0
- package/build/public/index.html +0 -49
- package/build/public/main.31f688a6254e294d9128.css +0 -118
- package/build/public/main.31f688a6254e294d9128.css.map +0 -1
- package/build/public/main.364c712f32662ff0e43d.css +0 -116
- package/build/public/main.364c712f32662ff0e43d.css.map +0 -1
- package/build/public/main.42ef3fe7e97347765c91.css +0 -118
- package/build/public/main.42ef3fe7e97347765c91.css.map +0 -1
- package/build/public/main.503446defafe7aeca2f3.css +0 -116
- package/build/public/main.503446defafe7aeca2f3.css.map +0 -1
- package/build/public/main.c50a3b5980fe26f78b65.css +0 -118
- package/build/public/main.c50a3b5980fe26f78b65.css.map +0 -1
- package/build/public/main.cda25e5813776c2af295.css +0 -116
- package/build/public/main.cda25e5813776c2af295.css.map +0 -1
- package/build/public/main.dbac3b290f78404ff579.css +0 -116
- package/build/public/main.dbac3b290f78404ff579.css.map +0 -1
- package/build/public/main.f8ce44e122e5e2b8f778.css +0 -118
- package/build/public/main.f8ce44e122e5e2b8f778.css.map +0 -1
- package/build/public/main.fc9ea7521a4aecce1a77.css +0 -118
- package/build/public/main.fc9ea7521a4aecce1a77.css.map +0 -1
- package/build/public/main.js +0 -403
- package/build/public/main.js.map +0 -1
- package/build/public/mainChunk.0a84f3ba4d2ab481e877.css +0 -116
- package/build/public/mainChunk.1b327d58afbddc917bce.css +0 -118
- package/build/public/mainChunk.2cdf583b2bd51aa67587.css +0 -118
- package/build/public/mainChunk.544af00f7e9ffcbe782c.css +0 -118
- package/build/public/mainChunk.56f96a13868b2b4a53be.css +0 -118
- package/build/public/mainChunk.5cee429bfbf06e7ecb39.css +0 -118
- package/build/public/mainChunk.a862474ed4cdb421ffa2.css +0 -118
- package/build/public/mainChunk.ada34153c4911b5ac22b.css +0 -118
- package/build/public/mainChunk.c16a8d01b2c49b2f2751.css +0 -118
- package/build/public/mainChunk.ca4a878d5478b9320be8.css +0 -116
- package/build/public/mainChunk.e3adc8758b4395546cef.css +0 -116
- package/build/public/mainChunk.f2c59fcbfc455d8b9de5.css +0 -118
- package/build/public/mainChunk.js +0 -396
- package/build/public/preview.html +0 -16
- package/build/public/previewBus.js +0 -2
- package/build/public/previewChunk.js +0 -2
- package/build/public/projects.html +0 -86
- package/build/public/server.html +0 -16
- package/build/public/serverBus.js +0 -2
- package/build/public/serverChunk.js +0 -2
- package/build/public/splash.html +0 -17
- package/build/public/studioBundle.828f34b1781061528841.css +0 -118
- package/build/public/studioBundle.bfe9138b2e0293fcb7da.css +0 -118
- package/build/public/studioBundle.c5b256eede880e502aac.css +0 -118
- package/build/public/studioBundle.e43df511c8e2e368900a.css +0 -118
- package/build/public/studioBundle.e69382554c2a2942ae32.css +0 -118
- package/build/public/studioBundle.ee21aa56a3999a2b380b.css +0 -118
- package/build/public/studioBundle.js +0 -396
- package/build/public/vercel.html +0 -16
- package/build/public/vercelChunk.js +0 -2
- package/build/xdg-open +0 -1066
- package/install/index.js +0 -5
- package/preinstall/index.js +0 -71
- package/prisma-client/README.md +0 -27
- package/prisma-client/generator-build/.DS_Store +0 -0
- package/prisma-client/generator-build/index.js +0 -79697
- package/prisma-client/index-browser.js +0 -3
- package/prisma-client/index.d.ts +0 -1
- package/prisma-client/index.js +0 -11
- package/prisma-client/package.json +0 -138
- package/prisma-client/runtime/index-browser.d.ts +0 -267
- package/prisma-client/runtime/index-browser.js +0 -2273
- package/prisma-client/runtime/index.d.ts +0 -1018
- package/prisma-client/runtime/index.js +0 -35028
- package/prisma-client/scripts/backup-index-browser.js +0 -3
- package/prisma-client/scripts/backup-index.d.ts +0 -1
- package/prisma-client/scripts/backup-index.js +0 -11
- package/prisma-client/scripts/colors.js +0 -180
- package/prisma-client/scripts/default-index-browser.js +0 -12
- package/prisma-client/scripts/default-index.d.ts +0 -47
- package/prisma-client/scripts/default-index.js +0 -12
- package/prisma-client/scripts/get-packed-client.js +0 -12
- package/prisma-client/scripts/mock-fs.js +0 -14
- package/prisma-client/scripts/postinstall.d.ts +0 -5
- package/prisma-client/scripts/postinstall.js +0 -398
- package/scripts/install-entry.js +0 -8
- package/scripts/preinstall-entry.js +0 -9
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { resolveBunEntrypoint } from "./bun-project.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { chmod, copyFile, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, rm, stat } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
import { BunBuild } from "@prisma/compute-sdk";
|
|
7
|
+
//#region src/lib/app/preview-build.ts
|
|
8
|
+
const NEXT_CONFIG_FILENAMES = [
|
|
9
|
+
"next.config.js",
|
|
10
|
+
"next.config.mjs",
|
|
11
|
+
"next.config.ts",
|
|
12
|
+
"next.config.mts"
|
|
13
|
+
];
|
|
14
|
+
var PreviewBuildStrategy = class {
|
|
15
|
+
#appPath;
|
|
16
|
+
#entrypoint;
|
|
17
|
+
#buildType;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.#appPath = options.appPath;
|
|
20
|
+
this.#entrypoint = options.entrypoint;
|
|
21
|
+
this.#buildType = options.buildType ?? "auto";
|
|
22
|
+
}
|
|
23
|
+
async canBuild() {
|
|
24
|
+
const { strategy } = await resolvePreviewBuildStrategy({
|
|
25
|
+
appPath: this.#appPath,
|
|
26
|
+
entrypoint: this.#entrypoint,
|
|
27
|
+
buildType: this.#buildType
|
|
28
|
+
});
|
|
29
|
+
return strategy.canBuild();
|
|
30
|
+
}
|
|
31
|
+
async execute() {
|
|
32
|
+
const { artifact } = await executePreviewBuild({
|
|
33
|
+
appPath: this.#appPath,
|
|
34
|
+
entrypoint: this.#entrypoint,
|
|
35
|
+
buildType: this.#buildType
|
|
36
|
+
});
|
|
37
|
+
return artifact;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
async function executePreviewBuild(options) {
|
|
41
|
+
const { strategy, buildType } = await resolvePreviewBuildStrategy({
|
|
42
|
+
appPath: options.appPath,
|
|
43
|
+
entrypoint: options.entrypoint,
|
|
44
|
+
buildType: options.buildType ?? "auto"
|
|
45
|
+
});
|
|
46
|
+
const artifact = await strategy.execute();
|
|
47
|
+
try {
|
|
48
|
+
await normalizeArtifactSymlinks(artifact.directory, options.appPath);
|
|
49
|
+
return {
|
|
50
|
+
artifact,
|
|
51
|
+
buildType
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
await artifact.cleanup?.().catch(() => void 0);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function resolvePreviewBuildStrategy(options) {
|
|
59
|
+
if (options.buildType === "nextjs") return {
|
|
60
|
+
buildType: "nextjs",
|
|
61
|
+
strategy: new PreviewNextjsBuild({ appPath: options.appPath })
|
|
62
|
+
};
|
|
63
|
+
if (options.buildType === "bun") {
|
|
64
|
+
const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint);
|
|
65
|
+
return {
|
|
66
|
+
buildType: "bun",
|
|
67
|
+
strategy: new BunBuild({
|
|
68
|
+
appPath: options.appPath,
|
|
69
|
+
entrypoint
|
|
70
|
+
})
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const nextjsStrategy = new PreviewNextjsBuild({ appPath: options.appPath });
|
|
74
|
+
if (await nextjsStrategy.canBuild()) return {
|
|
75
|
+
buildType: "nextjs",
|
|
76
|
+
strategy: nextjsStrategy
|
|
77
|
+
};
|
|
78
|
+
const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint);
|
|
79
|
+
return {
|
|
80
|
+
buildType: "bun",
|
|
81
|
+
strategy: new BunBuild({
|
|
82
|
+
appPath: options.appPath,
|
|
83
|
+
entrypoint
|
|
84
|
+
})
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
var PreviewNextjsBuild = class {
|
|
88
|
+
#appPath;
|
|
89
|
+
constructor(options) {
|
|
90
|
+
this.#appPath = options.appPath;
|
|
91
|
+
}
|
|
92
|
+
async canBuild() {
|
|
93
|
+
return await this.#hasNextConfig() || await this.#hasNextDependency();
|
|
94
|
+
}
|
|
95
|
+
async execute() {
|
|
96
|
+
await this.#runBuild();
|
|
97
|
+
const standaloneDir = path.join(this.#appPath, ".next", "standalone");
|
|
98
|
+
if (!(await stat(standaloneDir).catch(() => null))?.isDirectory()) throw new Error("Next.js build did not produce standalone output. Add output: \"standalone\" to your next.config file.");
|
|
99
|
+
const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
|
|
100
|
+
try {
|
|
101
|
+
const artifactDir = path.join(outDir, "app");
|
|
102
|
+
await stageNextjsStandaloneArtifact({
|
|
103
|
+
standaloneDir,
|
|
104
|
+
artifactDir,
|
|
105
|
+
appPath: this.#appPath
|
|
106
|
+
});
|
|
107
|
+
const publicDir = path.join(this.#appPath, "public");
|
|
108
|
+
if (await directoryExists(publicDir)) await cp(publicDir, path.join(artifactDir, "public"), { recursive: true });
|
|
109
|
+
const staticDir = path.join(this.#appPath, ".next", "static");
|
|
110
|
+
if (await directoryExists(staticDir)) await cp(staticDir, path.join(artifactDir, ".next", "static"), { recursive: true });
|
|
111
|
+
return {
|
|
112
|
+
directory: artifactDir,
|
|
113
|
+
entrypoint: "server.js",
|
|
114
|
+
defaultPortMapping: { http: 3e3 },
|
|
115
|
+
cleanup: () => rm(outDir, {
|
|
116
|
+
recursive: true,
|
|
117
|
+
force: true
|
|
118
|
+
})
|
|
119
|
+
};
|
|
120
|
+
} catch (error) {
|
|
121
|
+
await rm(outDir, {
|
|
122
|
+
recursive: true,
|
|
123
|
+
force: true
|
|
124
|
+
});
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async #hasNextConfig() {
|
|
129
|
+
let entries;
|
|
130
|
+
try {
|
|
131
|
+
entries = await readdir(this.#appPath);
|
|
132
|
+
} catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
return entries.some((entry) => NEXT_CONFIG_FILENAMES.includes(entry));
|
|
136
|
+
}
|
|
137
|
+
async #hasNextDependency() {
|
|
138
|
+
const packageJsonPath = path.join(this.#appPath, "package.json");
|
|
139
|
+
let content;
|
|
140
|
+
try {
|
|
141
|
+
content = await readFile(packageJsonPath, "utf8");
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = JSON.parse(content);
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const deps = isRecord(parsed.dependencies) ? parsed.dependencies : {};
|
|
152
|
+
const devDeps = isRecord(parsed.devDependencies) ? parsed.devDependencies : {};
|
|
153
|
+
return "next" in deps || "next" in devDeps;
|
|
154
|
+
}
|
|
155
|
+
async #runBuild() {
|
|
156
|
+
const candidates = [
|
|
157
|
+
{
|
|
158
|
+
command: path.join(this.#appPath, "node_modules", ".bin", "next"),
|
|
159
|
+
args: ["build"]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
command: "npx",
|
|
163
|
+
args: ["next", "build"]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
command: "bunx",
|
|
167
|
+
args: ["next", "build"]
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
for (const { command, args } of candidates) try {
|
|
171
|
+
await exec(command, args, this.#appPath);
|
|
172
|
+
return;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
throw new Error("Could not find the Next.js CLI. Install it with `npm install next` or ensure npx/bunx is available.");
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
async function stageNextjsStandaloneArtifact(options) {
|
|
181
|
+
const standaloneRoot = path.resolve(options.standaloneDir);
|
|
182
|
+
await copyPathMaterializingSymlinks(standaloneRoot, path.resolve(options.artifactDir), {
|
|
183
|
+
standaloneRoot,
|
|
184
|
+
appRoot: path.resolve(options.appPath)
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async function normalizeArtifactSymlinks(artifactDir, appPath) {
|
|
188
|
+
const normalizedArtifactDir = path.resolve(artifactDir);
|
|
189
|
+
const normalizedAppPath = path.resolve(appPath);
|
|
190
|
+
await walkDirectory(normalizedArtifactDir);
|
|
191
|
+
async function walkDirectory(directory) {
|
|
192
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
const fullPath = path.join(directory, entry.name);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
await walkDirectory(fullPath);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!entry.isSymbolicLink()) continue;
|
|
200
|
+
const target = await readlink(fullPath);
|
|
201
|
+
const resolvedTarget = path.resolve(path.dirname(fullPath), target);
|
|
202
|
+
if (isPathWithin(normalizedArtifactDir, resolvedTarget)) continue;
|
|
203
|
+
if (!isPathWithin(normalizedAppPath, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
|
|
204
|
+
const targetStat = await stat(resolvedTarget);
|
|
205
|
+
await rm(fullPath, {
|
|
206
|
+
force: true,
|
|
207
|
+
recursive: true
|
|
208
|
+
});
|
|
209
|
+
await cp(resolvedTarget, fullPath, {
|
|
210
|
+
recursive: targetStat.isDirectory(),
|
|
211
|
+
dereference: true
|
|
212
|
+
});
|
|
213
|
+
if (targetStat.isDirectory()) await walkDirectory(fullPath);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function isPathWithin(rootPath, candidatePath) {
|
|
218
|
+
const relativePath = path.relative(rootPath, candidatePath);
|
|
219
|
+
return relativePath === "" || !relativePath.startsWith(`..${path.sep}`) && relativePath !== ".." && !path.isAbsolute(relativePath);
|
|
220
|
+
}
|
|
221
|
+
async function copyPathMaterializingSymlinks(sourcePath, destinationPath, options) {
|
|
222
|
+
const sourceStat = await lstat(sourcePath);
|
|
223
|
+
if (sourceStat.isSymbolicLink()) {
|
|
224
|
+
await copyPathMaterializingSymlinks(await resolveSymlinkTarget(sourcePath, options), destinationPath, options);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (sourceStat.isDirectory()) {
|
|
228
|
+
await mkdir(destinationPath, { recursive: true });
|
|
229
|
+
const entries = await readdir(sourcePath, { withFileTypes: true });
|
|
230
|
+
for (const entry of entries) await copyPathMaterializingSymlinks(path.join(sourcePath, entry.name), path.join(destinationPath, entry.name), options);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (sourceStat.isFile()) {
|
|
234
|
+
await mkdir(path.dirname(destinationPath), { recursive: true });
|
|
235
|
+
await copyFile(sourcePath, destinationPath);
|
|
236
|
+
await chmod(destinationPath, sourceStat.mode);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function resolveSymlinkTarget(symlinkPath, options) {
|
|
240
|
+
const linkTarget = await readlink(symlinkPath);
|
|
241
|
+
const resolvedTarget = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
242
|
+
if (await pathExists(resolvedTarget)) {
|
|
243
|
+
if (!isPathWithin(options.appRoot, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
|
|
244
|
+
return resolvedTarget;
|
|
245
|
+
}
|
|
246
|
+
if (isPathWithin(options.standaloneRoot, resolvedTarget)) {
|
|
247
|
+
const fallbackTarget = path.join(options.appRoot, path.relative(options.standaloneRoot, resolvedTarget));
|
|
248
|
+
if (await pathExists(fallbackTarget)) return fallbackTarget;
|
|
249
|
+
}
|
|
250
|
+
throw new Error(`Next.js standalone symlink target is missing: ${symlinkPath} -> ${linkTarget} (resolved to ${resolvedTarget})`);
|
|
251
|
+
}
|
|
252
|
+
async function directoryExists(dirPath) {
|
|
253
|
+
return (await stat(dirPath).catch(() => null))?.isDirectory() ?? false;
|
|
254
|
+
}
|
|
255
|
+
function exec(command, args, cwd) {
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
execFile(command, args, { cwd }, (error, _stdout, stderr) => {
|
|
258
|
+
if (error) {
|
|
259
|
+
if ("code" in error && error.code === "ENOENT") {
|
|
260
|
+
reject(Object.assign(/* @__PURE__ */ new Error(`${command} not found`), { code: "ENOENT" }));
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const message = stderr.trim() || error.message;
|
|
264
|
+
reject(/* @__PURE__ */ new Error(`Next.js build failed:\n${message}`));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
resolve();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function isRecord(value) {
|
|
272
|
+
return typeof value === "object" && value !== null;
|
|
273
|
+
}
|
|
274
|
+
async function pathExists(targetPath) {
|
|
275
|
+
try {
|
|
276
|
+
await stat(targetPath);
|
|
277
|
+
return true;
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//#endregion
|
|
283
|
+
export { PreviewBuildStrategy, executePreviewBuild };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { selectPrompt, textPrompt } from "../../shell/prompt.js";
|
|
2
|
+
//#region src/lib/app/preview-interaction.ts
|
|
3
|
+
const CREATE_NEW_APP = "__create_new_app__";
|
|
4
|
+
const PREVIEW_DEFAULT_REGION = "eu-central-1";
|
|
5
|
+
function createPreviewDeployInteraction(context) {
|
|
6
|
+
return {
|
|
7
|
+
async selectService(services) {
|
|
8
|
+
const sorted = services.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
|
|
9
|
+
const selection = await selectPrompt({
|
|
10
|
+
input: context.runtime.stdin,
|
|
11
|
+
output: context.runtime.stderr,
|
|
12
|
+
message: "Select an app",
|
|
13
|
+
choices: [...sorted.map((service) => ({
|
|
14
|
+
label: service.name,
|
|
15
|
+
value: service.id
|
|
16
|
+
})), {
|
|
17
|
+
label: "Create a new app",
|
|
18
|
+
value: CREATE_NEW_APP
|
|
19
|
+
}]
|
|
20
|
+
});
|
|
21
|
+
return selection === CREATE_NEW_APP ? null : selection;
|
|
22
|
+
},
|
|
23
|
+
async provideServiceName() {
|
|
24
|
+
return textPrompt({
|
|
25
|
+
input: context.runtime.stdin,
|
|
26
|
+
output: context.runtime.stderr,
|
|
27
|
+
message: "App name",
|
|
28
|
+
placeholder: "hello-world",
|
|
29
|
+
validate: (value) => !value?.trim() ? "App name is required" : void 0
|
|
30
|
+
}).then((value) => value.trim());
|
|
31
|
+
},
|
|
32
|
+
async selectRegion(_regions) {
|
|
33
|
+
return PREVIEW_DEFAULT_REGION;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { PREVIEW_DEFAULT_REGION, createPreviewDeployInteraction };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//#region src/lib/app/preview-progress.ts
|
|
2
|
+
function createPreviewDeployProgress(output, enabled) {
|
|
3
|
+
if (!enabled) return;
|
|
4
|
+
const write = (line) => {
|
|
5
|
+
output.write(`${line}\n`);
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
onBuildStart() {
|
|
9
|
+
write("Building application...");
|
|
10
|
+
},
|
|
11
|
+
onBuildComplete() {
|
|
12
|
+
write("Build complete.");
|
|
13
|
+
},
|
|
14
|
+
onArchiveCreating() {
|
|
15
|
+
write("Creating deployment artifact...");
|
|
16
|
+
},
|
|
17
|
+
onArchiveReady(byteLength) {
|
|
18
|
+
write(`Artifact ready (${(byteLength / 1024).toFixed(1)} KB).`);
|
|
19
|
+
},
|
|
20
|
+
onVersionCreated(versionId) {
|
|
21
|
+
write(`Deployment ${versionId} created.`);
|
|
22
|
+
},
|
|
23
|
+
onUploadComplete() {
|
|
24
|
+
write("Upload complete.");
|
|
25
|
+
},
|
|
26
|
+
onStartRequested() {
|
|
27
|
+
write("Starting deployment...");
|
|
28
|
+
},
|
|
29
|
+
onStatusChange(status) {
|
|
30
|
+
write(`Status: ${status}`);
|
|
31
|
+
},
|
|
32
|
+
onRunning(url) {
|
|
33
|
+
if (url) {
|
|
34
|
+
write(`Deployment is running at ${url}.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
write("Deployment is running.");
|
|
38
|
+
},
|
|
39
|
+
onPromoteStart() {
|
|
40
|
+
write("Promoting deployment...");
|
|
41
|
+
},
|
|
42
|
+
onPromoted(url) {
|
|
43
|
+
if (url) {
|
|
44
|
+
write(`Promoted to ${url}.`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
write("Promotion complete.");
|
|
48
|
+
},
|
|
49
|
+
onPromoteFailed(error) {
|
|
50
|
+
write(`Promotion failed${error?.message ? `: ${error.message}` : "."}`);
|
|
51
|
+
},
|
|
52
|
+
onOldVersionStopping(versionId) {
|
|
53
|
+
write(`Stopping previous deployment ${versionId}...`);
|
|
54
|
+
},
|
|
55
|
+
onOldVersionStopped(versionId) {
|
|
56
|
+
write(`Previous deployment ${versionId} stopped.`);
|
|
57
|
+
},
|
|
58
|
+
onOldVersionStopFailed(versionId) {
|
|
59
|
+
write(`Failed to stop previous deployment ${versionId} (non-fatal).`);
|
|
60
|
+
},
|
|
61
|
+
onOldVersionDeleting(versionId) {
|
|
62
|
+
write(`Deleting previous deployment ${versionId}...`);
|
|
63
|
+
},
|
|
64
|
+
onOldVersionDeleted(versionId) {
|
|
65
|
+
write(`Previous deployment ${versionId} deleted.`);
|
|
66
|
+
},
|
|
67
|
+
onOldVersionDeleteFailed(versionId) {
|
|
68
|
+
write(`Failed to delete previous deployment ${versionId} (non-fatal).`);
|
|
69
|
+
},
|
|
70
|
+
onCleanupDanglingVersion(versionId) {
|
|
71
|
+
write(`Cleaning up deployment ${versionId}...`);
|
|
72
|
+
},
|
|
73
|
+
onCleanupDanglingVersionComplete(versionId) {
|
|
74
|
+
write(`Deployment ${versionId} cleaned up.`);
|
|
75
|
+
},
|
|
76
|
+
onCleanupDanglingVersionFailed(versionId) {
|
|
77
|
+
write(`Failed to clean up deployment ${versionId}.`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createPreviewPromoteProgress(output, enabled) {
|
|
82
|
+
if (!enabled) return;
|
|
83
|
+
const write = (line) => {
|
|
84
|
+
output.write(`${line}\n`);
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
onVersionStarting(versionId) {
|
|
88
|
+
write(`Starting deployment ${versionId}...`);
|
|
89
|
+
},
|
|
90
|
+
onVersionStartRequested() {
|
|
91
|
+
write("Requesting deployment start...");
|
|
92
|
+
},
|
|
93
|
+
onStatusChange(status) {
|
|
94
|
+
write(`Status: ${status}`);
|
|
95
|
+
},
|
|
96
|
+
onVersionRunning() {
|
|
97
|
+
write("Deployment is running.");
|
|
98
|
+
},
|
|
99
|
+
onPromoteStart() {
|
|
100
|
+
write("Promoting deployment...");
|
|
101
|
+
},
|
|
102
|
+
onPromoted(url) {
|
|
103
|
+
if (url) {
|
|
104
|
+
write(`Promoted to ${url}.`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
write("Promotion complete.");
|
|
108
|
+
},
|
|
109
|
+
onPromoteFailed(error) {
|
|
110
|
+
write(`Promotion failed${error?.message ? `: ${error.message}` : "."}`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createPreviewUpdateEnvProgress(output, enabled) {
|
|
115
|
+
if (!enabled) return;
|
|
116
|
+
const write = (line) => {
|
|
117
|
+
output.write(`${line}\n`);
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
onVersionCreated(versionId) {
|
|
121
|
+
write(`Creating updated deployment ${versionId}...`);
|
|
122
|
+
},
|
|
123
|
+
onStartRequested() {
|
|
124
|
+
write("Starting deployment...");
|
|
125
|
+
},
|
|
126
|
+
onStatusChange(status) {
|
|
127
|
+
write(`Status: ${status}`);
|
|
128
|
+
},
|
|
129
|
+
onRunning(url) {
|
|
130
|
+
if (url) {
|
|
131
|
+
write(`Deployment is running at ${url}.`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
write("Deployment is running.");
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { createPreviewDeployProgress, createPreviewPromoteProgress, createPreviewUpdateEnvProgress };
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { envVarNames } from "./env-vars.js";
|
|
2
|
+
import { PreviewBuildStrategy } from "./preview-build.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ApiError, ComputeClient } from "@prisma/compute-sdk";
|
|
5
|
+
//#region src/lib/app/preview-provider.ts
|
|
6
|
+
function createPreviewAppProvider(client) {
|
|
7
|
+
const sdk = new ComputeClient(client);
|
|
8
|
+
return {
|
|
9
|
+
async createProject(options) {
|
|
10
|
+
const projectResult = await sdk.createProject({ name: options.name });
|
|
11
|
+
if (projectResult.isErr()) throw new Error(projectResult.error.message);
|
|
12
|
+
return {
|
|
13
|
+
id: projectResult.value.id,
|
|
14
|
+
name: projectResult.value.name
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
async listApps(projectId) {
|
|
18
|
+
const servicesResult = await sdk.listServices({ projectId });
|
|
19
|
+
if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
|
|
20
|
+
return (await Promise.all(servicesResult.value.map(async (service) => {
|
|
21
|
+
const detailResult = await sdk.showService({ serviceId: service.id });
|
|
22
|
+
return detailResult.isOk() ? detailResult.value : {
|
|
23
|
+
id: service.id,
|
|
24
|
+
name: service.name,
|
|
25
|
+
region: service.region,
|
|
26
|
+
latestVersionId: null,
|
|
27
|
+
serviceEndpointDomain: void 0
|
|
28
|
+
};
|
|
29
|
+
}))).map((service) => ({
|
|
30
|
+
id: service.id,
|
|
31
|
+
name: service.name,
|
|
32
|
+
region: service.region ?? null,
|
|
33
|
+
liveDeploymentId: service.latestVersionId ?? null,
|
|
34
|
+
liveUrl: toAbsoluteUrl(service.serviceEndpointDomain ?? null)
|
|
35
|
+
}));
|
|
36
|
+
},
|
|
37
|
+
async removeApp(appId) {
|
|
38
|
+
const appResult = await sdk.showService({ serviceId: appId });
|
|
39
|
+
if (appResult.isErr()) throw new Error(appResult.error.message);
|
|
40
|
+
const destroyResult = await sdk.destroyService({
|
|
41
|
+
serviceId: appId,
|
|
42
|
+
keepService: false,
|
|
43
|
+
timeoutSeconds: 120,
|
|
44
|
+
pollIntervalMs: 2e3
|
|
45
|
+
});
|
|
46
|
+
if (destroyResult.isErr()) throw new Error(destroyResult.error.message);
|
|
47
|
+
return {
|
|
48
|
+
id: appResult.value.id,
|
|
49
|
+
name: appResult.value.name
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
async promoteDeployment(options) {
|
|
53
|
+
const promoteResult = await sdk.promote({
|
|
54
|
+
serviceId: options.appId,
|
|
55
|
+
versionId: options.deploymentId,
|
|
56
|
+
timeoutSeconds: 120,
|
|
57
|
+
pollIntervalMs: 2e3,
|
|
58
|
+
progress: options.progress
|
|
59
|
+
});
|
|
60
|
+
if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
|
|
61
|
+
},
|
|
62
|
+
async deployApp(options) {
|
|
63
|
+
const deployResult = await sdk.deploy({
|
|
64
|
+
strategy: new PreviewBuildStrategy({
|
|
65
|
+
appPath: path.resolve(options.cwd),
|
|
66
|
+
entrypoint: options.entrypoint,
|
|
67
|
+
buildType: options.buildType
|
|
68
|
+
}),
|
|
69
|
+
projectId: options.projectId,
|
|
70
|
+
serviceId: options.appId,
|
|
71
|
+
serviceName: options.appName,
|
|
72
|
+
region: options.region,
|
|
73
|
+
portMapping: options.portMapping,
|
|
74
|
+
envVars: options.envVars,
|
|
75
|
+
timeoutSeconds: 120,
|
|
76
|
+
pollIntervalMs: 2e3,
|
|
77
|
+
interaction: options.interaction,
|
|
78
|
+
progress: options.progress
|
|
79
|
+
});
|
|
80
|
+
if (deployResult.isErr()) throw new Error(deployResult.error.message);
|
|
81
|
+
const deployed = deployResult.value;
|
|
82
|
+
return {
|
|
83
|
+
projectId: deployed.projectId,
|
|
84
|
+
app: {
|
|
85
|
+
id: deployed.serviceId,
|
|
86
|
+
name: deployed.serviceName,
|
|
87
|
+
region: deployed.region ?? null,
|
|
88
|
+
liveDeploymentId: deployed.versionId,
|
|
89
|
+
liveUrl: toAbsoluteUrl(deployed.serviceEndpointDomain ?? null)
|
|
90
|
+
},
|
|
91
|
+
deployment: {
|
|
92
|
+
id: deployed.versionId,
|
|
93
|
+
status: "running",
|
|
94
|
+
url: toAbsoluteUrl(deployed.serviceEndpointDomain ?? deployed.versionEndpointDomain ?? null)
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
async updateAppEnv(options) {
|
|
99
|
+
const updateResult = await sdk.updateEnv({
|
|
100
|
+
serviceId: options.appId,
|
|
101
|
+
envVars: options.envVars,
|
|
102
|
+
timeoutSeconds: 120,
|
|
103
|
+
pollIntervalMs: 2e3,
|
|
104
|
+
progress: options.progress
|
|
105
|
+
});
|
|
106
|
+
if (updateResult.isErr()) throw new Error(updateResult.error.message);
|
|
107
|
+
const promoteResult = await sdk.promote({
|
|
108
|
+
serviceId: options.appId,
|
|
109
|
+
versionId: updateResult.value.versionId,
|
|
110
|
+
timeoutSeconds: 120,
|
|
111
|
+
pollIntervalMs: 2e3,
|
|
112
|
+
progress: options.promoteProgress
|
|
113
|
+
});
|
|
114
|
+
if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
|
|
115
|
+
const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: updateResult.value.versionId })]);
|
|
116
|
+
if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
|
|
117
|
+
if (versionResult.isErr()) throw new Error(versionResult.error.message);
|
|
118
|
+
return {
|
|
119
|
+
projectId: updateResult.value.projectId,
|
|
120
|
+
app: {
|
|
121
|
+
id: serviceResult.value.id,
|
|
122
|
+
name: serviceResult.value.name,
|
|
123
|
+
region: serviceResult.value.region ?? null,
|
|
124
|
+
liveDeploymentId: serviceResult.value.latestVersionId ?? null,
|
|
125
|
+
liveUrl: toAbsoluteUrl(serviceResult.value.serviceEndpointDomain ?? null)
|
|
126
|
+
},
|
|
127
|
+
deployment: {
|
|
128
|
+
id: versionResult.value.id,
|
|
129
|
+
status: versionResult.value.status,
|
|
130
|
+
createdAt: versionResult.value.createdAt,
|
|
131
|
+
url: toAbsoluteUrl(serviceResult.value.serviceEndpointDomain ?? versionResult.value.previewDomain ?? null),
|
|
132
|
+
live: true
|
|
133
|
+
},
|
|
134
|
+
variables: envVarNames(versionResult.value.envVars)
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
async listAppEnvNames(options) {
|
|
138
|
+
const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: options.deploymentId })]);
|
|
139
|
+
if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
|
|
140
|
+
if (versionResult.isErr()) throw new Error(versionResult.error.message);
|
|
141
|
+
return {
|
|
142
|
+
projectId: serviceResult.value.projectId,
|
|
143
|
+
app: {
|
|
144
|
+
id: serviceResult.value.id,
|
|
145
|
+
name: serviceResult.value.name,
|
|
146
|
+
region: serviceResult.value.region ?? null,
|
|
147
|
+
liveDeploymentId: serviceResult.value.latestVersionId ?? null,
|
|
148
|
+
liveUrl: toAbsoluteUrl(serviceResult.value.serviceEndpointDomain ?? null)
|
|
149
|
+
},
|
|
150
|
+
deployment: {
|
|
151
|
+
id: versionResult.value.id,
|
|
152
|
+
status: versionResult.value.status,
|
|
153
|
+
createdAt: versionResult.value.createdAt,
|
|
154
|
+
url: toAbsoluteUrl(versionResult.value.previewDomain ?? null),
|
|
155
|
+
live: serviceResult.value.latestVersionId === versionResult.value.id
|
|
156
|
+
},
|
|
157
|
+
variables: envVarNames(versionResult.value.envVars)
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
async listDeployments(appId) {
|
|
161
|
+
const [appResult, versionsResult] = await Promise.all([sdk.showService({ serviceId: appId }), sdk.listVersions({ serviceId: appId })]);
|
|
162
|
+
if (appResult.isErr()) throw new Error(appResult.error.message);
|
|
163
|
+
if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
|
|
164
|
+
return {
|
|
165
|
+
app: {
|
|
166
|
+
id: appResult.value.id,
|
|
167
|
+
name: appResult.value.name,
|
|
168
|
+
region: appResult.value.region ?? null,
|
|
169
|
+
liveDeploymentId: appResult.value.latestVersionId ?? null,
|
|
170
|
+
liveUrl: toAbsoluteUrl(appResult.value.serviceEndpointDomain ?? null)
|
|
171
|
+
},
|
|
172
|
+
deployments: versionsResult.value.slice().sort((left, right) => {
|
|
173
|
+
const byDate = right.createdAt.localeCompare(left.createdAt);
|
|
174
|
+
return byDate !== 0 ? byDate : right.id.localeCompare(left.id);
|
|
175
|
+
}).map((deployment) => ({
|
|
176
|
+
id: deployment.id,
|
|
177
|
+
status: deployment.status,
|
|
178
|
+
createdAt: deployment.createdAt,
|
|
179
|
+
url: toAbsoluteUrl(deployment.previewDomain ?? null),
|
|
180
|
+
live: null
|
|
181
|
+
}))
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
async showDeployment(deploymentId) {
|
|
185
|
+
const deploymentResult = await sdk.showVersion({ versionId: deploymentId });
|
|
186
|
+
if (deploymentResult.isErr()) {
|
|
187
|
+
if (ApiError.is(deploymentResult.error) && deploymentResult.error.statusCode === 404) return null;
|
|
188
|
+
throw new Error(deploymentResult.error.message);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
app: await findAppForDeployment(sdk, deploymentId),
|
|
192
|
+
deployment: {
|
|
193
|
+
id: deploymentResult.value.id,
|
|
194
|
+
status: deploymentResult.value.status,
|
|
195
|
+
createdAt: deploymentResult.value.createdAt,
|
|
196
|
+
url: toAbsoluteUrl(deploymentResult.value.previewDomain ?? null),
|
|
197
|
+
live: null
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async function findAppForDeployment(sdk, deploymentId) {
|
|
204
|
+
const projectsResult = await sdk.listProjects();
|
|
205
|
+
if (projectsResult.isErr()) throw new Error(projectsResult.error.message);
|
|
206
|
+
for (const project of projectsResult.value) {
|
|
207
|
+
const servicesResult = await sdk.listServices({ projectId: project.id });
|
|
208
|
+
if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
|
|
209
|
+
for (const service of servicesResult.value) {
|
|
210
|
+
const detailResult = await sdk.showService({ serviceId: service.id });
|
|
211
|
+
if (detailResult.isErr()) throw new Error(detailResult.error.message);
|
|
212
|
+
const app = {
|
|
213
|
+
id: detailResult.value.id,
|
|
214
|
+
name: detailResult.value.name,
|
|
215
|
+
region: detailResult.value.region ?? null,
|
|
216
|
+
liveDeploymentId: detailResult.value.latestVersionId ?? null,
|
|
217
|
+
liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
|
|
218
|
+
};
|
|
219
|
+
if (app.liveDeploymentId === deploymentId) return app;
|
|
220
|
+
const versionsResult = await sdk.listVersions({ serviceId: service.id });
|
|
221
|
+
if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
|
|
222
|
+
if (versionsResult.value.some((version) => version.id === deploymentId)) return app;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
function toAbsoluteUrl(url) {
|
|
228
|
+
if (!url) return null;
|
|
229
|
+
return url.startsWith("https://") || url.startsWith("http://") ? url : `https://${url}`;
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
export { createPreviewAppProvider };
|