@superblocksteam/sdk 2.0.3-next.103 → 2.0.3-next.105
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/cli-replacement/automatic-upgrades.d.ts +4 -0
- package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -0
- package/dist/cli-replacement/automatic-upgrades.js +227 -0
- package/dist/cli-replacement/automatic-upgrades.js.map +1 -0
- package/dist/cli-replacement/dev.d.mts +1 -6
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.mjs +2 -0
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/common.d.ts +6 -0
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/common.js.map +1 -1
- package/package.json +5 -4
- package/src/cli-replacement/automatic-upgrades.ts +315 -0
- package/src/cli-replacement/dev.mts +5 -7
- package/src/index.ts +2 -1
- package/src/types/common.ts +7 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import * as child_process from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { isNativeError } from "node:util/types";
|
|
4
|
+
import { isEqual, pickBy } from "lodash-es";
|
|
5
|
+
import { resolveCommand, type DetectResult } from "package-manager-detector";
|
|
6
|
+
import { detect } from "package-manager-detector/detect";
|
|
7
|
+
import gt from "semver/functions/gt.js";
|
|
8
|
+
import valid from "semver/functions/valid.js";
|
|
9
|
+
import { getLogger } from "../dev-utils/dev-logger.mjs";
|
|
10
|
+
import type { ResponseMeta } from "../socket/handlers.js";
|
|
11
|
+
import type { ApplicationConfig } from "../types/common.js";
|
|
12
|
+
import type { LockService } from "@superblocksteam/vite-plugin-file-sync/lock-service";
|
|
13
|
+
|
|
14
|
+
const exec = promisify(child_process.exec);
|
|
15
|
+
const logger = getLogger();
|
|
16
|
+
|
|
17
|
+
interface Versions {
|
|
18
|
+
[pkg: string]: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CheckVersionsErrorResponse {
|
|
22
|
+
responseMeta: ResponseMeta;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CheckVersionsSuccessResponse {
|
|
26
|
+
data: Versions;
|
|
27
|
+
responseMeta: ResponseMeta;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getRemoteCliLibraryVersions(
|
|
31
|
+
config: ApplicationConfig,
|
|
32
|
+
): Promise<Versions | undefined> {
|
|
33
|
+
const { token, superblocksBaseUrl, id } = config;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(
|
|
37
|
+
new URL(
|
|
38
|
+
`api/v3/applications/${id}/cli-library-versions`,
|
|
39
|
+
superblocksBaseUrl,
|
|
40
|
+
),
|
|
41
|
+
{
|
|
42
|
+
method: "GET",
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${token}`,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (response.ok) {
|
|
50
|
+
const data = (await response.json()) as CheckVersionsSuccessResponse;
|
|
51
|
+
return data.data;
|
|
52
|
+
} else {
|
|
53
|
+
const error = (await response.json()) as CheckVersionsErrorResponse;
|
|
54
|
+
console.log(
|
|
55
|
+
`Could not get latest CLI version: ${error.responseMeta.message}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (isNativeError(error)) {
|
|
60
|
+
logger.error(error.message);
|
|
61
|
+
} else {
|
|
62
|
+
logger.error(`${error}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function installPackageVersions(pm: DetectResult, versions: Versions) {
|
|
68
|
+
const installCommand = resolveCommand(
|
|
69
|
+
pm.agent,
|
|
70
|
+
"install",
|
|
71
|
+
Object.entries(versions).map(
|
|
72
|
+
([pkg, version]) => `@superblocksteam/${pkg}@${version}`,
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (!installCommand) {
|
|
77
|
+
console.error(
|
|
78
|
+
"We could not determine how to upgrade your Superblocks packages.",
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { command, args } = installCommand;
|
|
84
|
+
|
|
85
|
+
// upgrade packages
|
|
86
|
+
return await exec(`${command} ${args.join(" ")}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function findSuperblocksExecutable(): Promise<string | undefined> {
|
|
90
|
+
try {
|
|
91
|
+
// Use cross-platform approach to find superblocks executable
|
|
92
|
+
const command = process.platform === "win32" ? "where" : "which";
|
|
93
|
+
const { stdout } = await exec(`${command} superblocks`);
|
|
94
|
+
return stdout.trim();
|
|
95
|
+
} catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function getLocalCliLibraryVersions(
|
|
101
|
+
pm: DetectResult,
|
|
102
|
+
): Promise<Partial<Versions>> {
|
|
103
|
+
return {
|
|
104
|
+
cli: await getLocalCliVersion(),
|
|
105
|
+
library: await getLocalPackageVersion(pm, "library"),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function getLocalCliVersion(): Promise<string | undefined> {
|
|
110
|
+
try {
|
|
111
|
+
const superblocksPath = await findSuperblocksExecutable();
|
|
112
|
+
if (!superblocksPath) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { stdout } = await exec(`${superblocksPath} version --json`);
|
|
117
|
+
const json = JSON.parse(stdout) as Record<string, string>;
|
|
118
|
+
// Extract version from string like "@superblocksteam/cli/2.0.0-next.1"
|
|
119
|
+
return json.cliVersion?.replace("@superblocksteam/cli/", "");
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (isNativeError(error)) {
|
|
122
|
+
logger.error(`Error getting CLI version: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function getLocalPackageVersion(
|
|
129
|
+
pm: DetectResult,
|
|
130
|
+
pkg: string,
|
|
131
|
+
global?: boolean,
|
|
132
|
+
) {
|
|
133
|
+
try {
|
|
134
|
+
// Configure commands and parsing logic based on package manager type
|
|
135
|
+
switch (pm.agent) {
|
|
136
|
+
case "npm": {
|
|
137
|
+
const { stdout } = await exec(
|
|
138
|
+
`npm list ${global ? "--global" : ""} @superblocksteam/${pkg} --json`,
|
|
139
|
+
{
|
|
140
|
+
cwd: process.cwd(),
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
const parsed = JSON.parse(stdout);
|
|
144
|
+
return (
|
|
145
|
+
parsed.dependencies?.[`@superblocksteam/${pkg}`]?.version ??
|
|
146
|
+
parsed.devDependencies?.[`@superblocksteam/${pkg}`]?.version
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
case "pnpm": {
|
|
150
|
+
const { stdout } = await exec(
|
|
151
|
+
`pnpm list ${global ? "--global" : ""} @superblocksteam/${pkg} --json`,
|
|
152
|
+
{ cwd: process.cwd() },
|
|
153
|
+
);
|
|
154
|
+
const parsed = JSON.parse(stdout);
|
|
155
|
+
// Check the structure based on actual output
|
|
156
|
+
return (
|
|
157
|
+
parsed[0]?.dependencies?.[`@superblocksteam/${pkg}`]?.version ??
|
|
158
|
+
parsed[0]?.devDependencies?.[`@superblocksteam/${pkg}`]?.version
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
default:
|
|
162
|
+
console.error(
|
|
163
|
+
`${pm.agent} is currently not supported. Your application will not be upgraded automatically.`,
|
|
164
|
+
);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.error(
|
|
169
|
+
"We couldn't resolve your current Superblocks package versions. Your application will not be upgraded automatically.",
|
|
170
|
+
);
|
|
171
|
+
console.error(e);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if the CLI is updatable via the oclif update command
|
|
178
|
+
* This indicates it was installed through a prebuilt installer (and updates the CLI)
|
|
179
|
+
*/
|
|
180
|
+
async function tryCliUpdateCommand(version: string): Promise<boolean> {
|
|
181
|
+
try {
|
|
182
|
+
const superblocksPath = await findSuperblocksExecutable();
|
|
183
|
+
if (!superblocksPath) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check if the CLI is installed in a way that allows direct updates
|
|
188
|
+
// We'll try running the update command with --version to see if it shows an updatable message
|
|
189
|
+
const { stdout } = await exec(
|
|
190
|
+
`${superblocksPath} update --version=${version}`,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// If it mentions "not updatable" in either stdout or stderr, it's not a prebuilt installer
|
|
194
|
+
return !stdout.includes("not updatable");
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (isNativeError(error)) {
|
|
197
|
+
logger.error(`Error checking updatability: ${error.message}`);
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Restart the CLI
|
|
205
|
+
*/
|
|
206
|
+
async function restartCli() {
|
|
207
|
+
// Get the args that were used to start the current process
|
|
208
|
+
const args = process.argv.slice(1);
|
|
209
|
+
|
|
210
|
+
// Create a new process with the same arguments
|
|
211
|
+
const child = child_process.spawn(process.execPath, [...args], {
|
|
212
|
+
detached: true,
|
|
213
|
+
stdio: "inherit",
|
|
214
|
+
env: process.env,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Disconnect the child from the parent
|
|
218
|
+
child.unref();
|
|
219
|
+
|
|
220
|
+
// Exit the current process after the next tick to ensure any logs were written to stdout
|
|
221
|
+
process.nextTick(() => process.exit(0));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function checkVersionsAndUpgrade(
|
|
225
|
+
lockService: LockService,
|
|
226
|
+
config?: ApplicationConfig,
|
|
227
|
+
) {
|
|
228
|
+
const pm = await detect({
|
|
229
|
+
strategies: [
|
|
230
|
+
"packageManager-field",
|
|
231
|
+
"lockfile",
|
|
232
|
+
"install-metadata",
|
|
233
|
+
"devEngines-field",
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (!pm) return;
|
|
238
|
+
if (!config?.id) return;
|
|
239
|
+
|
|
240
|
+
const localVersions = await getLocalCliLibraryVersions(pm);
|
|
241
|
+
|
|
242
|
+
if (
|
|
243
|
+
// don't attempt upgrades in local development
|
|
244
|
+
Object.values(localVersions).some(
|
|
245
|
+
(x) => !x || !valid(x) || x.startsWith("file:") || x.startsWith("link:"),
|
|
246
|
+
)
|
|
247
|
+
) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const serverVersions = await getRemoteCliLibraryVersions(config);
|
|
252
|
+
|
|
253
|
+
// don't attempt upgrades if
|
|
254
|
+
if (
|
|
255
|
+
// server versions could not be fetched, or
|
|
256
|
+
!serverVersions ||
|
|
257
|
+
// we have a different # of libraries in server/local
|
|
258
|
+
Object.keys(localVersions).length !== Object.keys(serverVersions).length
|
|
259
|
+
) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const isUpToDate = isEqual(localVersions, serverVersions);
|
|
264
|
+
|
|
265
|
+
if (isUpToDate) return;
|
|
266
|
+
|
|
267
|
+
// Determine which packages need to be updated
|
|
268
|
+
const packagesToUpdate = pickBy(localVersions, (_, key) => {
|
|
269
|
+
try {
|
|
270
|
+
return gt(serverVersions[key], (localVersions as Versions)[key]);
|
|
271
|
+
} catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}) as Versions;
|
|
275
|
+
|
|
276
|
+
if (Object.keys(packagesToUpdate).length === 0) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let cliUpdated = false;
|
|
281
|
+
let libraryUpdated = false;
|
|
282
|
+
|
|
283
|
+
// Handle CLI update separately if it needs updating
|
|
284
|
+
if (packagesToUpdate.cli) {
|
|
285
|
+
// Try to update using Oclif's builtin update command
|
|
286
|
+
const isUpdated = await tryCliUpdateCommand(serverVersions.cli);
|
|
287
|
+
|
|
288
|
+
if (!isUpdated) {
|
|
289
|
+
// try to update using package manager (npm/pnpm/yarn)
|
|
290
|
+
await installPackageVersions(pm, { cli: serverVersions.cli });
|
|
291
|
+
cliUpdated = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Always use package manager for library updates
|
|
296
|
+
if (packagesToUpdate.library) {
|
|
297
|
+
await installPackageVersions(pm, { library: serverVersions.library });
|
|
298
|
+
libraryUpdated = true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (cliUpdated && libraryUpdated) {
|
|
302
|
+
logger.info(
|
|
303
|
+
"@superblocksteam/cli and @superblocksteam/library have been updated.",
|
|
304
|
+
);
|
|
305
|
+
} else if (cliUpdated) {
|
|
306
|
+
logger.info("@superblocksteam/cli has been updated.");
|
|
307
|
+
} else if (libraryUpdated) {
|
|
308
|
+
logger.info("@superblocksteam/library has been updated.");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
logger.info("Restarting the CLI…");
|
|
312
|
+
|
|
313
|
+
await lockService.releaseLock();
|
|
314
|
+
await restartCli();
|
|
315
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../dev-utils/dev-tracer.js";
|
|
2
|
+
|
|
2
3
|
import * as fsp from "node:fs/promises";
|
|
3
4
|
import { maskUnixSignals } from "@superblocksteam/util";
|
|
4
5
|
import { AiService } from "@superblocksteam/vite-plugin-file-sync/ai-service";
|
|
@@ -9,19 +10,15 @@ import {
|
|
|
9
10
|
import { OperationQueue } from "@superblocksteam/vite-plugin-file-sync/operation-queue";
|
|
10
11
|
import { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service";
|
|
11
12
|
import { green } from "colorette";
|
|
13
|
+
|
|
12
14
|
import { getLogger } from "../dev-utils/dev-logger.mjs";
|
|
13
15
|
import { createDevServer } from "../dev-utils/dev-server.mjs";
|
|
14
16
|
import { SuperblocksSdk } from "../sdk.js";
|
|
17
|
+
import { checkVersionsAndUpgrade } from "./automatic-upgrades.js";
|
|
15
18
|
import type { DevLogger } from "../dev-utils/dev-logger.mjs";
|
|
19
|
+
import type { ApplicationConfig } from "../types/common.js";
|
|
16
20
|
import type { DraftInterface } from "@superblocksteam/vite-plugin-file-sync/draft-interface";
|
|
17
21
|
|
|
18
|
-
export interface ApplicationConfig {
|
|
19
|
-
superblocksBaseUrl: string;
|
|
20
|
-
token: string;
|
|
21
|
-
id: string;
|
|
22
|
-
branchName: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
22
|
const passErrorToVSCode = (message: string, logger: DevLogger) => {
|
|
26
23
|
if (message) {
|
|
27
24
|
// Prefixing with `clierr:` will make the VS code extension capture this message and show it to the user.
|
|
@@ -105,6 +102,7 @@ export async function dev(options: {
|
|
|
105
102
|
if (lockService) {
|
|
106
103
|
try {
|
|
107
104
|
await lockService!.acquireLock();
|
|
105
|
+
await checkVersionsAndUpgrade(lockService, applicationConfig);
|
|
108
106
|
} catch (error) {
|
|
109
107
|
passErrorToVSCode(
|
|
110
108
|
(error as { context: { message: string } }).context.message,
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ export {
|
|
|
53
53
|
type ApiWithPb,
|
|
54
54
|
type AppToSign,
|
|
55
55
|
type AppToVerify,
|
|
56
|
+
type ApplicationConfig,
|
|
56
57
|
type ApplicationSettingsHashes,
|
|
57
58
|
type ApplicationSignatureTree,
|
|
58
59
|
type ApplicationSignatureTreeSigned,
|
|
@@ -113,7 +114,7 @@ export {
|
|
|
113
114
|
|
|
114
115
|
export { createDevServer } from "./dev-utils/dev-server.mjs";
|
|
115
116
|
|
|
116
|
-
export { dev
|
|
117
|
+
export { dev } from "./cli-replacement/dev.mjs";
|
|
117
118
|
export { fetchAndWriteApplication } from "./cli-replacement/init.js";
|
|
118
119
|
|
|
119
120
|
export { ExportViewMode } from "@superblocksteam/shared";
|
package/src/types/common.ts
CHANGED
|
@@ -14,6 +14,13 @@ export interface FlagBootstrap {
|
|
|
14
14
|
"superblocks.git.split.large.step.lines"?: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface ApplicationConfig {
|
|
18
|
+
superblocksBaseUrl: string;
|
|
19
|
+
token: string;
|
|
20
|
+
id: string;
|
|
21
|
+
branchName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
export type User = {
|
|
18
25
|
id: string;
|
|
19
26
|
email: string;
|