@superblocksteam/sdk 2.0.117-next.3 → 2.0.118-next.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.
Files changed (79) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/cli-replacement/dev.d.mts +6 -0
  3. package/dist/cli-replacement/dev.d.mts.map +1 -1
  4. package/dist/cli-replacement/dev.mjs +1 -0
  5. package/dist/cli-replacement/dev.mjs.map +1 -1
  6. package/dist/client.d.ts +67 -6
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +94 -0
  9. package/dist/client.js.map +1 -1
  10. package/dist/dev-utils/dedupe-async.d.ts +16 -0
  11. package/dist/dev-utils/dedupe-async.d.ts.map +1 -0
  12. package/dist/dev-utils/dedupe-async.js +27 -0
  13. package/dist/dev-utils/dedupe-async.js.map +1 -0
  14. package/dist/dev-utils/dedupe-async.test.d.ts +2 -0
  15. package/dist/dev-utils/dedupe-async.test.d.ts.map +1 -0
  16. package/dist/dev-utils/dedupe-async.test.js +120 -0
  17. package/dist/dev-utils/dedupe-async.test.js.map +1 -0
  18. package/dist/dev-utils/dev-server-metrics.d.mts +95 -0
  19. package/dist/dev-utils/dev-server-metrics.d.mts.map +1 -0
  20. package/dist/dev-utils/dev-server-metrics.mjs +193 -0
  21. package/dist/dev-utils/dev-server-metrics.mjs.map +1 -0
  22. package/dist/dev-utils/dev-server-persist.test.mjs +117 -17
  23. package/dist/dev-utils/dev-server-persist.test.mjs.map +1 -1
  24. package/dist/dev-utils/dev-server.d.mts +19 -1
  25. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  26. package/dist/dev-utils/dev-server.mjs +296 -28
  27. package/dist/dev-utils/dev-server.mjs.map +1 -1
  28. package/dist/flag.d.ts +1 -1
  29. package/dist/flag.d.ts.map +1 -1
  30. package/dist/flag.js +3 -3
  31. package/dist/flag.js.map +1 -1
  32. package/dist/index.d.ts +4 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +3 -2
  35. package/dist/index.js.map +1 -1
  36. package/dist/sdk.d.ts +8 -1
  37. package/dist/sdk.d.ts.map +1 -1
  38. package/dist/sdk.js +24 -1
  39. package/dist/sdk.js.map +1 -1
  40. package/dist/sdk.test.js +102 -1
  41. package/dist/sdk.test.js.map +1 -1
  42. package/dist/telemetry/index.d.ts +2 -1
  43. package/dist/telemetry/index.d.ts.map +1 -1
  44. package/dist/telemetry/index.js +8 -1
  45. package/dist/telemetry/index.js.map +1 -1
  46. package/dist/telemetry/logging.d.ts +1 -2
  47. package/dist/telemetry/logging.d.ts.map +1 -1
  48. package/dist/telemetry/logging.js +1 -1
  49. package/dist/telemetry/logging.js.map +1 -1
  50. package/dist/telemetry/memory-metrics.d.ts +3 -0
  51. package/dist/telemetry/memory-metrics.d.ts.map +1 -0
  52. package/dist/telemetry/memory-metrics.js +59 -0
  53. package/dist/telemetry/memory-metrics.js.map +1 -0
  54. package/dist/types/common.d.ts +1 -1
  55. package/dist/types/common.d.ts.map +1 -1
  56. package/dist/version-control.d.mts.map +1 -1
  57. package/dist/version-control.mjs +14 -19
  58. package/dist/version-control.mjs.map +1 -1
  59. package/eslint.config.js +6 -0
  60. package/package.json +8 -8
  61. package/src/cli-replacement/dev.mts +8 -0
  62. package/src/client.ts +189 -6
  63. package/src/dev-utils/dedupe-async.test.ts +151 -0
  64. package/src/dev-utils/dedupe-async.ts +45 -0
  65. package/src/dev-utils/dev-server-metrics.mts +252 -0
  66. package/src/dev-utils/dev-server-persist.test.mts +170 -19
  67. package/src/dev-utils/dev-server.mts +366 -32
  68. package/src/flag.ts +4 -4
  69. package/src/index.ts +19 -1
  70. package/src/sdk.test.ts +145 -6
  71. package/src/sdk.ts +36 -0
  72. package/src/telemetry/index.ts +9 -1
  73. package/src/telemetry/logging.ts +2 -2
  74. package/src/telemetry/memory-metrics.ts +90 -0
  75. package/src/types/common.ts +1 -1
  76. package/src/version-control.mts +11 -30
  77. package/test/version-control.test.mts +0 -2
  78. package/tsconfig.tsbuildinfo +1 -1
  79. package/turbo.json +1 -0
@@ -1,8 +1,11 @@
1
1
  import type * as NodeChildProcess from "node:child_process";
2
2
  import { EventEmitter } from "node:events";
3
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
3
6
  import { PassThrough } from "node:stream";
4
7
 
5
- import { describe, expect, it, vi } from "vitest";
8
+ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
6
9
 
7
10
  const { spawnMock } = vi.hoisted(() => ({
8
11
  spawnMock: vi.fn(),
@@ -22,6 +25,14 @@ vi.mock("node:child_process", async () => {
22
25
  };
23
26
  });
24
27
 
28
+ // Use a static import so the slow `dev-server.mjs` module graph (vite, react
29
+ // plugin, workspace deps) loads during file evaluation rather than inside each
30
+ // test, where the cold-import cost easily exceeds the default 5s test timeout.
31
+ import {
32
+ createWorkspacePersistArchive,
33
+ getSuperblocksTarExcludes,
34
+ } from "./dev-server.mjs";
35
+
25
36
  function createMockProcess() {
26
37
  return Object.assign(new EventEmitter(), {
27
38
  stdin: new PassThrough(),
@@ -30,8 +41,53 @@ function createMockProcess() {
30
41
  });
31
42
  }
32
43
 
44
+ describe("getSuperblocksTarExcludes", () => {
45
+ let tempRoot: string;
46
+
47
+ beforeAll(() => {
48
+ tempRoot = mkdtempSync(join(tmpdir(), "persist-test-"));
49
+ });
50
+
51
+ afterAll(() => {
52
+ rmSync(tempRoot, { recursive: true, force: true });
53
+ });
54
+
55
+ it("excludes .superblocks entirely when the directory does not exist", () => {
56
+ const result = getSuperblocksTarExcludes(tempRoot);
57
+ expect(result).toEqual(["--exclude", ".superblocks"]);
58
+ });
59
+
60
+ it("excludes subdirs not in the safelist and keeps context/drafts", () => {
61
+ const sb = join(tempRoot, ".superblocks");
62
+ mkdirSync(sb);
63
+ mkdirSync(join(sb, "context"));
64
+ mkdirSync(join(sb, "drafts"));
65
+ mkdirSync(join(sb, "recordings"));
66
+ mkdirSync(join(sb, "scratch"));
67
+ mkdirSync(join(sb, "system-skills"));
68
+
69
+ const result = getSuperblocksTarExcludes(tempRoot);
70
+
71
+ // Safelisted dirs must NOT appear
72
+ expect(result).not.toContain(".superblocks/context");
73
+ expect(result).not.toContain(".superblocks/drafts");
74
+
75
+ // Non-safelisted dirs must be excluded
76
+ expect(result).toContain(".superblocks/recordings");
77
+ expect(result).toContain(".superblocks/scratch");
78
+ expect(result).toContain(".superblocks/system-skills");
79
+
80
+ // Each excluded dir has a preceding --exclude flag
81
+ for (const name of ["recordings", "scratch", "system-skills"]) {
82
+ const idx = result.indexOf(`.superblocks/${name}`);
83
+ expect(idx).toBeGreaterThan(0);
84
+ expect(result[idx - 1]).toBe("--exclude");
85
+ }
86
+ });
87
+ });
88
+
33
89
  describe("createWorkspacePersistArchive", () => {
34
- it("excludes node_modules from the tar archive", async () => {
90
+ it("archive respects excludes", async () => {
35
91
  const tarProc = createMockProcess();
36
92
  const zstdProc = createMockProcess();
37
93
  spawnMock.mockImplementation((command: string) => {
@@ -47,27 +103,17 @@ describe("createWorkspacePersistArchive", () => {
47
103
  return zstdProc;
48
104
  });
49
105
 
50
- const { createWorkspacePersistArchive } = await import("./dev-server.mjs");
51
-
52
106
  await expect(createWorkspacePersistArchive("/workspace")).resolves.toEqual(
53
107
  Buffer.from("archive"),
54
108
  );
55
109
 
56
110
  const tarCall = spawnMock.mock.calls.find(([command]) => command === "tar");
57
111
  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
- ]);
112
+ const args: string[] = tarCall?.[1];
113
+
114
+ // Standard excludes are always present
115
+ expect(args).toContain(".git");
116
+ expect(args).toContain("node_modules");
71
117
  });
72
118
 
73
119
  it("rejects if tar fails after zstd closes successfully", async () => {
@@ -87,10 +133,115 @@ describe("createWorkspacePersistArchive", () => {
87
133
  return zstdProc;
88
134
  });
89
135
 
90
- const { createWorkspacePersistArchive } = await import("./dev-server.mjs");
91
-
92
136
  await expect(
93
137
  createWorkspacePersistArchive("/workspace"),
94
138
  ).rejects.toThrowError(/^tar archival failed \(code 2\): tar failed$/);
95
139
  });
96
140
  });
141
+
142
+ describe("createWorkspacePersistUploadHeaders", () => {
143
+ it("forwards presigned upload headers and adds the archive content length", async () => {
144
+ const { createWorkspacePersistUploadHeaders } =
145
+ await import("./dev-server.mjs");
146
+
147
+ expect(
148
+ createWorkspacePersistUploadHeaders(123, {
149
+ "Content-Type": "application/custom-zstd",
150
+ "x-amz-server-side-encryption": "AES256",
151
+ }),
152
+ ).toEqual({
153
+ "content-length": "123",
154
+ "content-type": "application/custom-zstd",
155
+ "x-amz-server-side-encryption": "AES256",
156
+ });
157
+ });
158
+
159
+ it("normalizes presigned upload header names case-insensitively", async () => {
160
+ const { createWorkspacePersistUploadHeaders } =
161
+ await import("./dev-server.mjs");
162
+
163
+ expect(
164
+ createWorkspacePersistUploadHeaders(789, {
165
+ "content-type": "application/custom-zstd",
166
+ "content-length": "1",
167
+ "x-amz-server-side-encryption": "AES256",
168
+ }),
169
+ ).toEqual({
170
+ "content-length": "789",
171
+ "content-type": "application/custom-zstd",
172
+ "x-amz-server-side-encryption": "AES256",
173
+ });
174
+ });
175
+
176
+ it("keeps the legacy content headers when SABS sends only a URL", async () => {
177
+ const { createWorkspacePersistUploadHeaders } =
178
+ await import("./dev-server.mjs");
179
+
180
+ expect(createWorkspacePersistUploadHeaders(456)).toEqual({
181
+ "content-length": "456",
182
+ "content-type": "application/zstd",
183
+ });
184
+ });
185
+ });
186
+
187
+ describe("isWorkspacePersistUploadHeaders", () => {
188
+ it("accepts undefined (legacy SABS payloads with no uploadHeaders)", async () => {
189
+ const { isWorkspacePersistUploadHeaders } =
190
+ await import("./dev-server.mjs");
191
+
192
+ expect(isWorkspacePersistUploadHeaders(undefined)).toBe(true);
193
+ });
194
+
195
+ it("accepts a valid string map", async () => {
196
+ const { isWorkspacePersistUploadHeaders } =
197
+ await import("./dev-server.mjs");
198
+
199
+ expect(
200
+ isWorkspacePersistUploadHeaders({
201
+ "content-type": "application/zstd",
202
+ "x-amz-server-side-encryption": "AES256",
203
+ }),
204
+ ).toBe(true);
205
+ });
206
+
207
+ it("rejects null, arrays, and non-string values", async () => {
208
+ const { isWorkspacePersistUploadHeaders } =
209
+ await import("./dev-server.mjs");
210
+
211
+ expect(isWorkspacePersistUploadHeaders(null)).toBe(false);
212
+ expect(isWorkspacePersistUploadHeaders([])).toBe(false);
213
+ expect(isWorkspacePersistUploadHeaders({ "x-foo": 1 })).toBe(false);
214
+ });
215
+
216
+ it("rejects case-insensitive duplicate keys", async () => {
217
+ const { isWorkspacePersistUploadHeaders } =
218
+ await import("./dev-server.mjs");
219
+
220
+ expect(
221
+ isWorkspacePersistUploadHeaders({
222
+ "Content-Type": "application/zstd",
223
+ "content-type": "application/custom-zstd",
224
+ }),
225
+ ).toBe(false);
226
+ });
227
+
228
+ it("rejects header names with illegal characters", async () => {
229
+ const { isWorkspacePersistUploadHeaders } =
230
+ await import("./dev-server.mjs");
231
+
232
+ // Spaces are not allowed in HTTP token names.
233
+ expect(isWorkspacePersistUploadHeaders({ "bad header": "value" })).toBe(
234
+ false,
235
+ );
236
+ });
237
+
238
+ it("rejects header values with illegal characters", async () => {
239
+ const { isWorkspacePersistUploadHeaders } =
240
+ await import("./dev-server.mjs");
241
+
242
+ // CR/LF in header values is not allowed (header injection guard).
243
+ expect(isWorkspacePersistUploadHeaders({ "x-foo": "bad\r\nvalue" })).toBe(
244
+ false,
245
+ );
246
+ });
247
+ });