@superblocksteam/sdk 2.0.110 → 2.0.111
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/.turbo/turbo-build.log +1 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts +1 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.js +4 -2
- package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.test.d.ts +11 -0
- package/dist/cli-replacement/automatic-upgrades.test.d.ts.map +1 -0
- package/dist/cli-replacement/automatic-upgrades.test.js +175 -0
- package/dist/cli-replacement/automatic-upgrades.test.js.map +1 -0
- package/dist/cli-replacement/dev.d.mts +12 -1
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.mjs +21 -3
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/dev-utils/dev-server.d.mts +9 -1
- package/dist/dev-utils/dev-server.d.mts.map +1 -1
- package/dist/dev-utils/dev-server.mjs +287 -75
- package/dist/dev-utils/dev-server.mjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/cli-replacement/automatic-upgrades.test.ts +251 -0
- package/src/cli-replacement/automatic-upgrades.ts +6 -1
- package/src/cli-replacement/dev.mts +27 -2
- package/src/dev-utils/dev-server.mts +358 -81
- package/src/index.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for checkVersionsAndWritePackageJson, specifically around the
|
|
3
|
+
* skipCliUpgrade parameter introduced for warm pool activation.
|
|
4
|
+
*
|
|
5
|
+
* Warm pods ship with the latest CLI, so an auto-upgrade would pointlessly
|
|
6
|
+
* restart the process and defeat the warm pool. But we still need the
|
|
7
|
+
* package.json version sync when resuming an existing app so the user's
|
|
8
|
+
* @superblocksteam/library pin matches the target version.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from "node:fs/promises";
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
14
|
+
|
|
15
|
+
import { checkVersionsAndWritePackageJson } from "./automatic-upgrades.js";
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Mocks
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
vi.mock("node:fs/promises", () => ({
|
|
22
|
+
default: {
|
|
23
|
+
readFile: vi.fn(),
|
|
24
|
+
writeFile: vi.fn(),
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock("node:child_process", () => ({
|
|
29
|
+
exec: vi.fn(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const upgradeWithPackageManagerCalls: unknown[] = [];
|
|
33
|
+
vi.mock("package-manager-detector/detect", () => ({
|
|
34
|
+
detect: vi.fn(async () => ({
|
|
35
|
+
name: "pnpm",
|
|
36
|
+
agent: "pnpm",
|
|
37
|
+
version: "10.0.0",
|
|
38
|
+
})),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
vi.mock("package-manager-detector", () => ({
|
|
42
|
+
resolveCommand: vi.fn(() => ({ command: "pnpm", args: ["install"] })),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
vi.mock("./version-detection.js", () => ({
|
|
46
|
+
getCurrentCliVersion: vi.fn(async () => ({
|
|
47
|
+
version: "2.0.0",
|
|
48
|
+
alias: undefined,
|
|
49
|
+
})),
|
|
50
|
+
clearCliVersionCache: vi.fn(),
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
vi.mock("../telemetry/logging.js", () => ({
|
|
54
|
+
getLogger: () => ({
|
|
55
|
+
info: vi.fn(),
|
|
56
|
+
warn: vi.fn(),
|
|
57
|
+
error: vi.fn(),
|
|
58
|
+
debug: vi.fn(),
|
|
59
|
+
}),
|
|
60
|
+
getErrorMeta: vi.fn(() => ({})),
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
vi.mock("../telemetry/index.js", () => ({
|
|
64
|
+
getTracer: () => ({
|
|
65
|
+
startActiveSpan: vi.fn((_name, fn) => fn({ end: vi.fn() })),
|
|
66
|
+
}),
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
vi.mock("@superblocksteam/shared", async () => {
|
|
70
|
+
return {
|
|
71
|
+
NON_SB_ORG_UPDATE_ERROR: "non_sb_org_update_error",
|
|
72
|
+
// traceFunction in tests is a passthrough — we just want the fn to run.
|
|
73
|
+
traceFunction: async ({ fn }: { fn: () => Promise<unknown> }) => {
|
|
74
|
+
return await fn();
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Test fixtures
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
const TARGET_CLI_VERSION = "2.1.0"; // newer than the mocked current 2.0.0
|
|
84
|
+
const TARGET_LIBRARY_VERSION = "1.9.0";
|
|
85
|
+
|
|
86
|
+
const mockLockService = {
|
|
87
|
+
shutdown: vi.fn(async () => undefined),
|
|
88
|
+
} as unknown as Parameters<typeof checkVersionsAndWritePackageJson>[0];
|
|
89
|
+
|
|
90
|
+
const mockConfig: Parameters<typeof checkVersionsAndWritePackageJson>[1] = {
|
|
91
|
+
id: "app-123",
|
|
92
|
+
branchName: "main",
|
|
93
|
+
superblocksBaseUrl: "https://example.test",
|
|
94
|
+
token: "test-token",
|
|
95
|
+
userId: "user-1",
|
|
96
|
+
userEmail: "test@example.com",
|
|
97
|
+
organizationId: "org-1",
|
|
98
|
+
featureFlags: {},
|
|
99
|
+
} as unknown as Parameters<typeof checkVersionsAndWritePackageJson>[1];
|
|
100
|
+
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
vi.clearAllMocks();
|
|
103
|
+
upgradeWithPackageManagerCalls.length = 0;
|
|
104
|
+
|
|
105
|
+
// Mock global fetch used by getRemoteVersions
|
|
106
|
+
global.fetch = vi.fn(async () => ({
|
|
107
|
+
ok: true,
|
|
108
|
+
json: async () => ({
|
|
109
|
+
data: {
|
|
110
|
+
cli: TARGET_CLI_VERSION,
|
|
111
|
+
library: TARGET_LIBRARY_VERSION,
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
})) as unknown as typeof fetch;
|
|
115
|
+
|
|
116
|
+
// Default package.json content
|
|
117
|
+
vi.mocked(fs.readFile).mockResolvedValue(
|
|
118
|
+
JSON.stringify({
|
|
119
|
+
name: "test-app",
|
|
120
|
+
dependencies: {
|
|
121
|
+
"@superblocksteam/library": "1.0.0",
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Tests
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
describe("checkVersionsAndWritePackageJson", () => {
|
|
133
|
+
describe("skipCliUpgrade=true (warm pool activation)", () => {
|
|
134
|
+
it("still writes package.json with the target library version", async () => {
|
|
135
|
+
await checkVersionsAndWritePackageJson(
|
|
136
|
+
mockLockService,
|
|
137
|
+
mockConfig,
|
|
138
|
+
false, // forceUpgrade
|
|
139
|
+
true, // skipCliUpgrade — the warm pool case
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
143
|
+
const [, writtenContent] = vi.mocked(fs.writeFile).mock.calls[0];
|
|
144
|
+
const parsed = JSON.parse(writtenContent as string);
|
|
145
|
+
expect(parsed.dependencies["@superblocksteam/library"]).toBe(
|
|
146
|
+
TARGET_LIBRARY_VERSION,
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("does NOT call the oclif CLI upgrader even when CLI is stale", async () => {
|
|
151
|
+
const { exec } = await import("node:child_process");
|
|
152
|
+
// If an upgrade were attempted, upgradeCliWithOclif would exec
|
|
153
|
+
// `which superblocks`. We assert that does not happen.
|
|
154
|
+
|
|
155
|
+
await checkVersionsAndWritePackageJson(
|
|
156
|
+
mockLockService,
|
|
157
|
+
mockConfig,
|
|
158
|
+
false,
|
|
159
|
+
true,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(exec).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("returns cliUpdated=false regardless of CLI version gap", async () => {
|
|
166
|
+
const result = await checkVersionsAndWritePackageJson(
|
|
167
|
+
mockLockService,
|
|
168
|
+
mockConfig,
|
|
169
|
+
false,
|
|
170
|
+
true,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(result.cliUpdated).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("skipCliUpgrade overrides forceUpgrade", async () => {
|
|
177
|
+
const { exec } = await import("node:child_process");
|
|
178
|
+
|
|
179
|
+
await checkVersionsAndWritePackageJson(
|
|
180
|
+
mockLockService,
|
|
181
|
+
mockConfig,
|
|
182
|
+
true, // forceUpgrade — should be overridden by skipCliUpgrade
|
|
183
|
+
true, // skipCliUpgrade wins
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(exec).not.toHaveBeenCalled();
|
|
187
|
+
// But package.json still gets written
|
|
188
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("skipCliUpgrade=false (default)", () => {
|
|
193
|
+
it("writes package.json with the target library version", async () => {
|
|
194
|
+
await checkVersionsAndWritePackageJson(
|
|
195
|
+
mockLockService,
|
|
196
|
+
mockConfig,
|
|
197
|
+
false,
|
|
198
|
+
false,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
202
|
+
const [, writtenContent] = vi.mocked(fs.writeFile).mock.calls[0];
|
|
203
|
+
const parsed = JSON.parse(writtenContent as string);
|
|
204
|
+
expect(parsed.dependencies["@superblocksteam/library"]).toBe(
|
|
205
|
+
TARGET_LIBRARY_VERSION,
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("defaults to false when omitted (backward compat)", async () => {
|
|
210
|
+
// 3-arg call (pre-warm-pool signature) should behave as skipCliUpgrade=false
|
|
211
|
+
const result = await checkVersionsAndWritePackageJson(
|
|
212
|
+
mockLockService,
|
|
213
|
+
mockConfig,
|
|
214
|
+
false,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Still writes package.json
|
|
218
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
219
|
+
expect(result).toBeDefined();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("SNAPSHOT library versions", () => {
|
|
224
|
+
it("pins to ephemeral alias when library target is a SNAPSHOT", async () => {
|
|
225
|
+
const snapshotVersion = "1.9.0-SNAPSHOT.abc123";
|
|
226
|
+
global.fetch = vi.fn(async () => ({
|
|
227
|
+
ok: true,
|
|
228
|
+
json: async () => ({
|
|
229
|
+
data: {
|
|
230
|
+
cli: TARGET_CLI_VERSION,
|
|
231
|
+
library: snapshotVersion,
|
|
232
|
+
},
|
|
233
|
+
}),
|
|
234
|
+
})) as unknown as typeof fetch;
|
|
235
|
+
|
|
236
|
+
await checkVersionsAndWritePackageJson(
|
|
237
|
+
mockLockService,
|
|
238
|
+
mockConfig,
|
|
239
|
+
false,
|
|
240
|
+
true, // warm pool
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
244
|
+
const [, writtenContent] = vi.mocked(fs.writeFile).mock.calls[0];
|
|
245
|
+
const parsed = JSON.parse(writtenContent as string);
|
|
246
|
+
expect(parsed.dependencies["@superblocksteam/library"]).toBe(
|
|
247
|
+
`npm:@superblocksteam/library-ephemeral@${snapshotVersion}`,
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -181,6 +181,7 @@ export async function checkVersionsAndWritePackageJson(
|
|
|
181
181
|
lockService: LockService,
|
|
182
182
|
config: ApplicationConfigWithTokenConfigAndUserInfo,
|
|
183
183
|
forceUpgrade: boolean = false,
|
|
184
|
+
skipCliUpgrade: boolean = false,
|
|
184
185
|
): Promise<{
|
|
185
186
|
cliUpdated: boolean;
|
|
186
187
|
upgradePromises: Promise<void>[];
|
|
@@ -269,7 +270,11 @@ export async function checkVersionsAndWritePackageJson(
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
try {
|
|
272
|
-
if (
|
|
273
|
+
if (
|
|
274
|
+
!skipCliUpgrade &&
|
|
275
|
+
(cliNeedsUpgrade || forceUpgrade) &&
|
|
276
|
+
currentCliInfo
|
|
277
|
+
) {
|
|
273
278
|
const cliUpgradePromise = traceFunction({
|
|
274
279
|
name: "upgradeCli",
|
|
275
280
|
tracer,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as child_process from "node:child_process";
|
|
2
2
|
import * as nodeFs from "node:fs/promises";
|
|
3
|
+
import type { Server as HttpServer } from "node:http";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import * as readline from "node:readline";
|
|
5
6
|
import { promisify } from "node:util";
|
|
@@ -248,6 +249,14 @@ async function installPackages(cwd: string, logger: Logger) {
|
|
|
248
249
|
export enum DevServerAutoUpgradeMode {
|
|
249
250
|
SKIP = "skip-upgrade",
|
|
250
251
|
FORCE = "force-upgrade",
|
|
252
|
+
/**
|
|
253
|
+
* Skip CLI auto-upgrade but still run the package.json version sync.
|
|
254
|
+
* Used by warm pool activation — the warm pod ships with the latest CLI
|
|
255
|
+
* so an auto-upgrade would pointlessly restart the process, but the user's
|
|
256
|
+
* application package.json still needs to be pinned to the target
|
|
257
|
+
* @superblocksteam/library version.
|
|
258
|
+
*/
|
|
259
|
+
SKIP_CLI_ONLY = "skip-cli-only",
|
|
251
260
|
}
|
|
252
261
|
|
|
253
262
|
export async function dev(options: {
|
|
@@ -284,6 +293,9 @@ export async function dev(options: {
|
|
|
284
293
|
|
|
285
294
|
/** Pre-fetched application data from the CLI validation step (avoids a duplicate network call). */
|
|
286
295
|
prefetchedApplication?: MultiPageApplicationWrapper;
|
|
296
|
+
|
|
297
|
+
/** Pre-existing HTTP server from warm standby mode (avoids port gap on transition). */
|
|
298
|
+
existingServer?: HttpServer;
|
|
287
299
|
}) {
|
|
288
300
|
const {
|
|
289
301
|
cwd,
|
|
@@ -311,6 +323,9 @@ export async function dev(options: {
|
|
|
311
323
|
const tracer = getTracer();
|
|
312
324
|
const logger = getLogger(options.logger);
|
|
313
325
|
const skipAutoUpgrade = autoUpgradeMode === DevServerAutoUpgradeMode.SKIP;
|
|
326
|
+
const skipCliUpgrade =
|
|
327
|
+
skipAutoUpgrade ||
|
|
328
|
+
autoUpgradeMode === DevServerAutoUpgradeMode.SKIP_CLI_ONLY;
|
|
314
329
|
|
|
315
330
|
await tracer.startActiveSpan("devServerStartup", async (startupSpan) => {
|
|
316
331
|
try {
|
|
@@ -663,8 +678,13 @@ export async function dev(options: {
|
|
|
663
678
|
let upgradePromises: Promise<void>[] = [];
|
|
664
679
|
const forceUpgrade =
|
|
665
680
|
options.autoUpgradeMode === DevServerAutoUpgradeMode.FORCE;
|
|
666
|
-
//
|
|
667
|
-
|
|
681
|
+
// Run version check when we haven't fully disabled auto-upgrades,
|
|
682
|
+
// when force is requested, or when only the CLI upgrade is skipped
|
|
683
|
+
// (warm pool still needs the package.json sync).
|
|
684
|
+
const skipCliOnly =
|
|
685
|
+
options.autoUpgradeMode ===
|
|
686
|
+
DevServerAutoUpgradeMode.SKIP_CLI_ONLY;
|
|
687
|
+
if (!skipAutoUpgrade || forceUpgrade || skipCliOnly) {
|
|
668
688
|
await tracer.startActiveSpan(
|
|
669
689
|
"versionCheckAndUpgrade",
|
|
670
690
|
async (span) => {
|
|
@@ -684,6 +704,7 @@ export async function dev(options: {
|
|
|
684
704
|
lockService,
|
|
685
705
|
applicationConfigWithTokenConfigAndUserInfo,
|
|
686
706
|
forceUpgrade,
|
|
707
|
+
skipCliUpgrade,
|
|
687
708
|
);
|
|
688
709
|
hasCliUpdated = result.cliUpdated;
|
|
689
710
|
upgradePromises = result.upgradePromises;
|
|
@@ -886,6 +907,10 @@ export async function dev(options: {
|
|
|
886
907
|
logger: options.logger,
|
|
887
908
|
sdk,
|
|
888
909
|
superblocksBaseUrl: tokenConfig.superblocksBaseUrl,
|
|
910
|
+
existingServer: options.existingServer,
|
|
911
|
+
// TODO: Remove this cast — build the options object to match
|
|
912
|
+
// CreateDevServerOptions directly so new required fields cause a
|
|
913
|
+
// compile error instead of silently passing undefined.
|
|
889
914
|
} as unknown as Parameters<typeof createDevServer>[0];
|
|
890
915
|
const result = await createDevServer(createDevServerOptions);
|
|
891
916
|
span.end();
|