@superblocksteam/sdk 2.0.115-next.1 → 2.0.115

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.
Files changed (38) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/cli-replacement/dev-s3-restore.test.d.mts +2 -0
  3. package/dist/cli-replacement/dev-s3-restore.test.d.mts.map +1 -0
  4. package/dist/cli-replacement/dev-s3-restore.test.mjs +457 -0
  5. package/dist/cli-replacement/dev-s3-restore.test.mjs.map +1 -0
  6. package/dist/cli-replacement/dev.d.mts +7 -0
  7. package/dist/cli-replacement/dev.d.mts.map +1 -1
  8. package/dist/cli-replacement/dev.mjs +49 -2
  9. package/dist/cli-replacement/dev.mjs.map +1 -1
  10. package/dist/cli-replacement/package-json-snapshot.d.mts +26 -0
  11. package/dist/cli-replacement/package-json-snapshot.d.mts.map +1 -0
  12. package/dist/cli-replacement/package-json-snapshot.mjs +222 -0
  13. package/dist/cli-replacement/package-json-snapshot.mjs.map +1 -0
  14. package/dist/cli-replacement/package-json-snapshot.test.d.mts +2 -0
  15. package/dist/cli-replacement/package-json-snapshot.test.d.mts.map +1 -0
  16. package/dist/cli-replacement/package-json-snapshot.test.mjs +207 -0
  17. package/dist/cli-replacement/package-json-snapshot.test.mjs.map +1 -0
  18. package/dist/dev-utils/dev-server-persist.test.d.mts +2 -0
  19. package/dist/dev-utils/dev-server-persist.test.d.mts.map +1 -0
  20. package/dist/dev-utils/dev-server-persist.test.mjs +77 -0
  21. package/dist/dev-utils/dev-server-persist.test.mjs.map +1 -0
  22. package/dist/dev-utils/dev-server.d.mts +1 -0
  23. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  24. package/dist/dev-utils/dev-server.mjs +75 -53
  25. package/dist/dev-utils/dev-server.mjs.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/package.json +6 -6
  31. package/src/cli-replacement/dev-s3-restore.test.mts +599 -0
  32. package/src/cli-replacement/dev.mts +91 -2
  33. package/src/cli-replacement/package-json-snapshot.mts +328 -0
  34. package/src/cli-replacement/package-json-snapshot.test.mts +250 -0
  35. package/src/dev-utils/dev-server-persist.test.mts +96 -0
  36. package/src/dev-utils/dev-server.mts +91 -73
  37. package/src/index.ts +15 -0
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,96 @@
1
+ import type * as NodeChildProcess from "node:child_process";
2
+ import { EventEmitter } from "node:events";
3
+ import { PassThrough } from "node:stream";
4
+
5
+ import { describe, expect, it, vi } from "vitest";
6
+
7
+ const { spawnMock } = vi.hoisted(() => ({
8
+ spawnMock: vi.fn(),
9
+ }));
10
+
11
+ vi.mock("node:child_process", async () => {
12
+ const actual =
13
+ await vi.importActual<typeof NodeChildProcess>("node:child_process");
14
+
15
+ return {
16
+ ...actual,
17
+ default: {
18
+ ...actual,
19
+ spawn: spawnMock,
20
+ },
21
+ spawn: spawnMock,
22
+ };
23
+ });
24
+
25
+ function createMockProcess() {
26
+ return Object.assign(new EventEmitter(), {
27
+ stdin: new PassThrough(),
28
+ stdout: new PassThrough(),
29
+ stderr: new PassThrough(),
30
+ });
31
+ }
32
+
33
+ describe("createWorkspacePersistArchive", () => {
34
+ it("excludes node_modules from the tar archive", async () => {
35
+ const tarProc = createMockProcess();
36
+ const zstdProc = createMockProcess();
37
+ spawnMock.mockImplementation((command: string) => {
38
+ if (command === "tar") {
39
+ queueMicrotask(() => tarProc.emit("close", 0));
40
+ return tarProc;
41
+ }
42
+
43
+ queueMicrotask(() => {
44
+ zstdProc.stdout.emit("data", Buffer.from("archive"));
45
+ zstdProc.emit("close", 0);
46
+ });
47
+ return zstdProc;
48
+ });
49
+
50
+ const { createWorkspacePersistArchive } = await import("./dev-server.mjs");
51
+
52
+ await expect(createWorkspacePersistArchive("/workspace")).resolves.toEqual(
53
+ Buffer.from("archive"),
54
+ );
55
+
56
+ const tarCall = spawnMock.mock.calls.find(([command]) => command === "tar");
57
+ expect(tarCall).toBeDefined();
58
+ expect(tarCall?.[1]).toEqual([
59
+ "cf",
60
+ "-",
61
+ "--exclude",
62
+ ".git",
63
+ "--exclude",
64
+ "node_modules",
65
+ "--exclude",
66
+ ".superblocks",
67
+ "-C",
68
+ "/workspace",
69
+ ".",
70
+ ]);
71
+ });
72
+
73
+ it("rejects if tar fails after zstd closes successfully", async () => {
74
+ const tarProc = createMockProcess();
75
+ const zstdProc = createMockProcess();
76
+ spawnMock.mockImplementation((command: string) => {
77
+ if (command === "tar") {
78
+ return tarProc;
79
+ }
80
+
81
+ queueMicrotask(() => {
82
+ zstdProc.emit("close", 0);
83
+ zstdProc.stderr.emit("data", Buffer.from("zstd diagnostic"));
84
+ tarProc.stderr.emit("data", Buffer.from("tar failed"));
85
+ tarProc.emit("close", 2);
86
+ });
87
+ return zstdProc;
88
+ });
89
+
90
+ const { createWorkspacePersistArchive } = await import("./dev-server.mjs");
91
+
92
+ await expect(
93
+ createWorkspacePersistArchive("/workspace"),
94
+ ).rejects.toThrowError(/^tar archival failed \(code 2\): tar failed$/);
95
+ });
96
+ });
@@ -123,6 +123,96 @@ function vitePlugins(
123
123
  ];
124
124
  }
125
125
 
126
+ export async function createWorkspacePersistArchive(root: string) {
127
+ // Warm pods already have template dependencies installed, so persist only
128
+ // user workspace files and let startup reconcile package drift if needed.
129
+ return new Promise<Buffer>((resolveArchive, rejectArchive) => {
130
+ const tarProc = child_process.spawn(
131
+ "tar",
132
+ [
133
+ "cf",
134
+ "-",
135
+ "--exclude",
136
+ ".git",
137
+ "--exclude",
138
+ "node_modules",
139
+ "--exclude",
140
+ ".superblocks",
141
+ "-C",
142
+ root,
143
+ ".",
144
+ ],
145
+ { stdio: ["ignore", "pipe", "pipe"] },
146
+ );
147
+ const zstdProc = child_process.spawn("zstd", ["-1", "--no-progress"], {
148
+ stdio: ["pipe", "pipe", "pipe"],
149
+ });
150
+ let settled = false;
151
+ let tarExitCode: number | null | undefined;
152
+ let zstdExitCode: number | null | undefined;
153
+ const rejectOnce = (error: Error) => {
154
+ if (settled) {
155
+ return;
156
+ }
157
+ settled = true;
158
+ rejectArchive(error);
159
+ };
160
+
161
+ tarProc.stdout.pipe(zstdProc.stdin);
162
+
163
+ // Attach error handlers on the piped stdin. ChildProcess-level 'error'
164
+ // events don't catch stream errors like EPIPE from writing to a closed
165
+ // pipe if the downstream process crashes.
166
+ zstdProc.stdin.on("error", (err) => {
167
+ rejectOnce(new Error(`zstd stdin stream error: ${err.message}`));
168
+ });
169
+
170
+ const chunks: Buffer[] = [];
171
+ zstdProc.stdout.on("data", (chunk: Buffer) => chunks.push(chunk));
172
+
173
+ let tarStderr = "";
174
+ let zstdStderr = "";
175
+ tarProc.stderr.on("data", (chunk: Buffer) => {
176
+ tarStderr += chunk.toString();
177
+ });
178
+ zstdProc.stderr.on("data", (chunk: Buffer) => {
179
+ zstdStderr += chunk.toString();
180
+ });
181
+
182
+ const finishIfClosed = () => {
183
+ if (settled || tarExitCode === undefined || zstdExitCode === undefined) {
184
+ return;
185
+ }
186
+ settled = true;
187
+ if (zstdExitCode !== 0) {
188
+ rejectArchive(
189
+ new Error(
190
+ `zstd compression failed (code ${zstdExitCode}): ${zstdStderr}`,
191
+ ),
192
+ );
193
+ } else if (tarExitCode !== 0) {
194
+ rejectArchive(
195
+ new Error(`tar archival failed (code ${tarExitCode}): ${tarStderr}`),
196
+ );
197
+ } else {
198
+ resolveArchive(Buffer.concat(chunks));
199
+ }
200
+ };
201
+
202
+ tarProc.on("close", (code) => {
203
+ tarExitCode = code;
204
+ finishIfClosed();
205
+ });
206
+
207
+ zstdProc.on("close", (code) => {
208
+ zstdExitCode = code;
209
+ finishIfClosed();
210
+ });
211
+ tarProc.on("error", rejectOnce);
212
+ zstdProc.on("error", rejectOnce);
213
+ });
214
+ }
215
+
126
216
  export const RESTART_EXIT_CODE = 98;
127
217
 
128
218
  function getJwksUriWithBaseUrl(superblocksBaseUrl?: string): string {
@@ -591,79 +681,7 @@ export async function createDevServer({
591
681
 
592
682
  logger.info("/_sb_persist: archiving and uploading workspace to S3...");
593
683
 
594
- // Create tar+zstd archive excluding .git/ and node_modules/.cache/
595
- // then stream directly to S3 via presigned PUT URL.
596
- const archive = await new Promise<Buffer>(
597
- (resolveArchive, rejectArchive) => {
598
- const tarProc = child_process.spawn(
599
- "tar",
600
- [
601
- "cf",
602
- "-",
603
- "--exclude",
604
- ".git",
605
- "--exclude",
606
- "node_modules/.cache",
607
- "--exclude",
608
- ".superblocks",
609
- "-C",
610
- process.cwd(),
611
- ".",
612
- ],
613
- { stdio: ["ignore", "pipe", "pipe"] },
614
- );
615
- const zstdProc = child_process.spawn(
616
- "zstd",
617
- ["-1", "--no-progress"],
618
- {
619
- stdio: ["pipe", "pipe", "pipe"],
620
- },
621
- );
622
-
623
- tarProc.stdout.pipe(zstdProc.stdin);
624
-
625
- // Attach error handlers on the piped stdin. ChildProcess-level 'error'
626
- // events don't catch stream errors like EPIPE from writing to a closed
627
- // pipe if the downstream process crashes.
628
- zstdProc.stdin.on("error", (err) => {
629
- rejectArchive(new Error(`zstd stdin stream error: ${err.message}`));
630
- });
631
-
632
- const chunks: Buffer[] = [];
633
- zstdProc.stdout.on("data", (chunk: Buffer) => chunks.push(chunk));
634
-
635
- let stderr = "";
636
- tarProc.stderr.on("data", (chunk: Buffer) => {
637
- stderr += chunk.toString();
638
- });
639
- zstdProc.stderr.on("data", (chunk: Buffer) => {
640
- stderr += chunk.toString();
641
- });
642
-
643
- let tarExitCode: number | null = null;
644
- tarProc.on("close", (code) => {
645
- tarExitCode = code;
646
- });
647
-
648
- zstdProc.on("close", (code) => {
649
- if (code !== 0) {
650
- rejectArchive(
651
- new Error(`zstd compression failed (code ${code}): ${stderr}`),
652
- );
653
- } else if (tarExitCode !== null && tarExitCode !== 0) {
654
- rejectArchive(
655
- new Error(
656
- `tar archival failed (code ${tarExitCode}): ${stderr}`,
657
- ),
658
- );
659
- } else {
660
- resolveArchive(Buffer.concat(chunks));
661
- }
662
- });
663
- tarProc.on("error", rejectArchive);
664
- zstdProc.on("error", rejectArchive);
665
- },
666
- );
684
+ const archive = await createWorkspacePersistArchive(process.cwd());
667
685
 
668
686
  logger.info(
669
687
  `/_sb_persist: archive created (${(archive.length / 1024 / 1024).toFixed(1)}MB) in ${Date.now() - persistStart}ms, uploading...`,
package/src/index.ts CHANGED
@@ -86,6 +86,21 @@ export {
86
86
 
87
87
  export { dev, DevServerAutoUpgradeMode } from "./cli-replacement/dev.mjs";
88
88
 
89
+ export {
90
+ didPackageJsonSnapshotChange,
91
+ packageJsonSnapshot,
92
+ packageJsonSnapshotDiagnostic,
93
+ readPackageJsonSnapshot,
94
+ readPackageJsonSnapshotWithSource,
95
+ PACKAGE_DEPENDENCY_FIELDS,
96
+ restoreManagedPackageDependencies,
97
+ MANAGED_PACKAGE_DEPENDENCIES,
98
+ SUPERBLOCKS_SDK_API_PACKAGE,
99
+ SUPERBLOCKS_LIBRARY_PACKAGE,
100
+ type PackageJsonSnapshot,
101
+ type PackageJsonSnapshotReadResult,
102
+ } from "./cli-replacement/package-json-snapshot.mjs";
103
+
89
104
  export { getLogger } from "./telemetry/logging.js";
90
105
 
91
106
  export {