@superblocksteam/sdk 2.0.6 → 2.0.7

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 (57) hide show
  1. package/dist/application-build.mjs +6 -2
  2. package/dist/application-build.mjs.map +1 -1
  3. package/dist/cli-replacement/automatic-upgrades.d.ts +5 -2
  4. package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -1
  5. package/dist/cli-replacement/automatic-upgrades.js +221 -93
  6. package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
  7. package/dist/cli-replacement/dev.d.mts +1 -2
  8. package/dist/cli-replacement/dev.d.mts.map +1 -1
  9. package/dist/cli-replacement/dev.mjs +57 -32
  10. package/dist/cli-replacement/dev.mjs.map +1 -1
  11. package/dist/client.d.ts +34 -0
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +92 -1
  14. package/dist/client.js.map +1 -1
  15. package/dist/dev-utils/dev-logger.d.mts +17 -7
  16. package/dist/dev-utils/dev-logger.d.mts.map +1 -1
  17. package/dist/dev-utils/dev-logger.mjs +50 -9
  18. package/dist/dev-utils/dev-logger.mjs.map +1 -1
  19. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  20. package/dist/dev-utils/dev-server.mjs +8 -12
  21. package/dist/dev-utils/dev-server.mjs.map +1 -1
  22. package/dist/dev-utils/dev-tracer.d.ts +2 -0
  23. package/dist/dev-utils/dev-tracer.d.ts.map +1 -1
  24. package/dist/dev-utils/dev-tracer.js +42 -35
  25. package/dist/dev-utils/dev-tracer.js.map +1 -1
  26. package/dist/dev-utils/vite-plugin-build-manifest-stub.d.mts +10 -0
  27. package/dist/dev-utils/vite-plugin-build-manifest-stub.d.mts.map +1 -0
  28. package/dist/dev-utils/vite-plugin-build-manifest-stub.mjs +27 -0
  29. package/dist/dev-utils/vite-plugin-build-manifest-stub.mjs.map +1 -0
  30. package/dist/dev-utils/vite-plugin-sb-cdn.d.mts.map +1 -1
  31. package/dist/dev-utils/vite-plugin-sb-cdn.mjs +13 -3
  32. package/dist/dev-utils/vite-plugin-sb-cdn.mjs.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/vite-plugin-generate-build-manifest.d.mts +21 -0
  38. package/dist/vite-plugin-generate-build-manifest.d.mts.map +1 -0
  39. package/dist/vite-plugin-generate-build-manifest.mjs +130 -0
  40. package/dist/vite-plugin-generate-build-manifest.mjs.map +1 -0
  41. package/dist/vite-plugin-inject-sb-ids-transform.mjs +1 -1
  42. package/dist/vite-plugin-inject-sb-ids-transform.mjs.map +1 -1
  43. package/package.json +6 -4
  44. package/src/application-build.mts +6 -3
  45. package/src/cli-replacement/automatic-upgrades.ts +278 -113
  46. package/src/cli-replacement/dev.mts +77 -43
  47. package/src/client.ts +115 -0
  48. package/src/dev-utils/dev-logger.mts +94 -20
  49. package/src/dev-utils/dev-server.mts +10 -12
  50. package/src/dev-utils/dev-tracer.ts +48 -37
  51. package/src/dev-utils/vite-plugin-build-manifest-stub.mts +30 -0
  52. package/src/dev-utils/vite-plugin-sb-cdn.mts +14 -3
  53. package/src/index.ts +4 -0
  54. package/src/vite-plugin-generate-build-manifest.mts +192 -0
  55. package/src/vite-plugin-inject-sb-ids-transform.mts +1 -1
  56. package/test/clients.test.mts +120 -0
  57. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,6 @@
1
1
  import "../dev-utils/dev-tracer.js";
2
2
 
3
3
  import * as child_process from "node:child_process";
4
- import * as fsp from "node:fs/promises";
5
4
  import path from "node:path";
6
5
  import { promisify } from "node:util";
7
6
  import { maskUnixSignals } from "@superblocksteam/util";
@@ -18,15 +17,16 @@ import fs from "fs-extra";
18
17
  import { resolveCommand } from "package-manager-detector";
19
18
  import { detect } from "package-manager-detector/detect";
20
19
 
21
- import { getLogger } from "../dev-utils/dev-logger.mjs";
20
+ import { getErrorMeta, getLogger } from "../dev-utils/dev-logger.mjs";
22
21
  import { createDevServer } from "../dev-utils/dev-server.mjs";
23
- import tracer from "../dev-utils/dev-tracer.js";
22
+ import tracer, { initializeTracingSdk } from "../dev-utils/dev-tracer.js";
24
23
  import { SuperblocksSdk } from "../sdk.js";
25
24
  import {
26
25
  checkVersionsAndUpgrade,
27
26
  getCurrentCliVersion,
28
27
  getCurrentLibraryVersionWithoutPM,
29
28
  } from "./automatic-upgrades.js";
29
+ import { AUTO_UPGRADE_EXIT_CODE } from "./automatic-upgrades.js";
30
30
  import type { DevLogger } from "../dev-utils/dev-logger.mjs";
31
31
  import type { ApplicationConfig } from "../types/common.js";
32
32
  import type { DraftInterface } from "@superblocksteam/vite-plugin-file-sync/draft-interface";
@@ -93,7 +93,6 @@ async function installPackages(cwd: string, logger: DevLogger) {
93
93
  export async function dev(options: {
94
94
  /* cwd is required */
95
95
  cwd: string;
96
- pidfilePath?: string;
97
96
  devServerPort?: number;
98
97
 
99
98
  /* user control of sync operations */
@@ -107,7 +106,7 @@ export async function dev(options: {
107
106
  /* For debugging purposes to get location of the cli */
108
107
  superblocksPath?: string;
109
108
  /* For redirecting output, like when running outside of the CLI */
110
- logger?: (message: string) => void;
109
+ logger?: (...messages: (string | Error)[]) => void;
111
110
 
112
111
  /* For a child process when restarting the dev server for automatic upgrades */
113
112
  skipAutoUpgrade?: boolean;
@@ -119,7 +118,6 @@ export async function dev(options: {
119
118
 
120
119
  const {
121
120
  cwd,
122
- pidfilePath,
123
121
  devServerPort,
124
122
  downloadFirst,
125
123
  uploadFirst,
@@ -127,12 +125,7 @@ export async function dev(options: {
127
125
  applicationConfig,
128
126
  skipAutoUpgrade,
129
127
  } = options;
130
-
131
- const cliVersion = await getCurrentCliVersion();
132
- const libraryVersion = await getCurrentLibraryVersionWithoutPM();
133
- logger.info(
134
- `Running command: ${options.superblocksPath} dev${options.uploadFirst ? " --upload-first" : ""}${options.downloadFirst ? " --download-first" : ""} with baseUrl: ${applicationConfig?.superblocksBaseUrl}, cliVersion: ${cliVersion}, libraryVersion: ${libraryVersion?.alias} ${libraryVersion?.version}`,
135
- );
128
+ initializeTracingSdk(applicationConfig);
136
129
 
137
130
  // Add check for node_modules
138
131
  if (!fs.existsSync(path.join(cwd, "node_modules"))) {
@@ -141,9 +134,13 @@ export async function dev(options: {
141
134
  );
142
135
  }
143
136
 
144
- if (pidfilePath) {
145
- await fsp.writeFile(pidfilePath, `${process.pid}\n`);
146
- }
137
+ logger.info("Starting dev server");
138
+
139
+ const cliVersion = await getCurrentCliVersion();
140
+ const libraryVersion = await getCurrentLibraryVersionWithoutPM();
141
+ logger.info(
142
+ `Running command: ${options.superblocksPath} dev${options.uploadFirst ? " --upload-first" : ""}${options.downloadFirst ? " --download-first" : ""} with baseUrl: ${applicationConfig?.superblocksBaseUrl}, cliVersion: ${cliVersion?.alias} ${cliVersion?.version}, libraryVersion: ${libraryVersion?.alias} ${libraryVersion?.version}`,
143
+ );
147
144
 
148
145
  const port = devServerPort ?? 5173;
149
146
 
@@ -188,17 +185,11 @@ export async function dev(options: {
188
185
  if (lockService) {
189
186
  try {
190
187
  await lockService!.acquireLock();
191
-
192
- // TODO(code-mode): package naming is preventing upgrade in ephemeral environments
193
- if (
194
- !process.env.PACKAGE_SUFFIX ||
195
- process.env.PACKAGE_SUFFIX === "" ||
196
- !skipAutoUpgrade
197
- ) {
198
- await checkVersionsAndUpgrade(lockService, applicationConfig);
199
- }
200
188
  } catch (error) {
201
- logger.error("Failed to acquire lock on application", error);
189
+ logger.error(
190
+ "Failed to acquire lock on application",
191
+ getErrorMeta(error),
192
+ );
202
193
  passErrorToVSCode(
203
194
  (error as { context?: { message: string } }).context?.message,
204
195
  logger,
@@ -222,7 +213,6 @@ export async function dev(options: {
222
213
  appRootDirPath: options.cwd,
223
214
  applicationId: applicationConfig.id,
224
215
  organizationId: currentUser.organizations[0].id,
225
- anthropicApiKey: process.env.ANTHROPIC_API_KEY || "",
226
216
  fsOperationQueue,
227
217
  draftInterface: syncService! as DraftInterface,
228
218
  tracer,
@@ -231,12 +221,15 @@ export async function dev(options: {
231
221
  const isSynced = localContents.hash === serverHash;
232
222
 
233
223
  if (isSynced) {
234
- logger.info("Local files are in sync with the server");
235
- return;
224
+ logger.info(
225
+ `Local files are in sync with the server on branch '${applicationConfig.branchName}'`,
226
+ );
227
+ } else {
228
+ logger.info(
229
+ `Local files are out of sync with the server on branch '${applicationConfig.branchName}'`,
230
+ );
236
231
  }
237
232
 
238
- logger.info("Local files are out of sync with the server");
239
-
240
233
  if (!(downloadFirst || uploadFirst)) {
241
234
  throw new Error(
242
235
  "You must choose --download-first or --upload-first to use the dev command",
@@ -246,37 +239,74 @@ export async function dev(options: {
246
239
  throw new Error("Choose either --download-first or --upload-first");
247
240
  }
248
241
 
242
+ let hasPackageChanged = false;
243
+
249
244
  if (downloadFirst) {
250
245
  logger.info(
251
- "Starting directory sync from server before starting the local server",
246
+ `Starting download of branch '${applicationConfig.branchName}'`,
252
247
  );
253
248
 
254
- // Read package.json before download
255
249
  const packageJsonBefore = await readPkgJson(cwd);
256
-
257
250
  await syncService!.downloadDirectory();
258
-
259
- // Read package.json after download and compare
260
251
  const packageJsonAfter = await readPkgJson(cwd);
261
252
 
262
- // Check if package.json changed
263
253
  if (packageJsonBefore && packageJsonAfter) {
264
254
  const diff = diffJson(packageJsonBefore, packageJsonAfter);
265
- const hasChanges = diff.some((part) => part.added || part.removed);
255
+ hasPackageChanged = diff.some((part) => part.added || part.removed);
256
+ } else if (packageJsonAfter) {
257
+ hasPackageChanged = true;
258
+ logger.info("package.json was created, installing packages…");
259
+ }
260
+ }
266
261
 
267
- if (hasChanges) {
268
- logger.info("package.json has changed, installing packages…");
269
- await installPackages(cwd, logger);
262
+ let hasCliUpdated = false;
263
+ if (
264
+ !process.env.PACKAGE_SUFFIX ||
265
+ process.env.PACKAGE_SUFFIX === "" ||
266
+ !skipAutoUpgrade
267
+ ) {
268
+ if (lockService) {
269
+ const result = await checkVersionsAndUpgrade(
270
+ lockService,
271
+ applicationConfig,
272
+ );
273
+ if (result?.libraryUpdated) {
274
+ hasPackageChanged = true;
270
275
  }
276
+ hasCliUpdated = result?.cliUpdated ?? false;
277
+ } else {
278
+ logger.warn(
279
+ "Lock service is not available, skipping version check and upgrade",
280
+ );
271
281
  }
272
- } else if (uploadFirst) {
273
- logger.info("Uploading local files to server before starting");
282
+ }
283
+
284
+ if (hasPackageChanged) {
285
+ logger.info("Installing packages…");
286
+ await installPackages(cwd, logger);
287
+ }
288
+
289
+ if (hasPackageChanged || uploadFirst) {
290
+ logger.info(
291
+ `Uploading local files to branch '${applicationConfig.branchName}' on server before starting`,
292
+ );
274
293
  await syncService!.uploadDirectory();
294
+ await syncService!.uploadDirectoryNowIfNeeded();
295
+ }
296
+
297
+ if (hasCliUpdated) {
298
+ logger.info("CLI was updated, restarting the dev server…");
299
+ process.exit(AUTO_UPGRADE_EXIT_CODE);
275
300
  }
276
301
  });
277
302
  } catch (error: any) {
278
303
  logger.error(error.message);
279
- process.exit(1);
304
+ try {
305
+ await lockService?.shutdownAndExit();
306
+ } finally {
307
+ // this is redundant, but it's here to make sure the lock service is shutdown and the process exits
308
+ process.exit(1);
309
+ }
280
310
  }
281
311
  } else {
282
312
  logger.info("Skipping directory sync");
@@ -296,6 +326,10 @@ export async function dev(options: {
296
326
  options.signal?.addEventListener("abort", () => {
297
327
  logger.info("Server closed");
298
328
  httpServer.close();
329
+ lockService?.shutdownAndExit().catch(() => {
330
+ // this is redundant, but it's here to make sure the lock service is shutdown and the process exits
331
+ process.exit(1);
332
+ });
299
333
  });
300
334
 
301
335
  logger.debug(green(`Local server started at port ${port}`));
package/src/client.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  NotFoundError,
12
12
  BadRequestError,
13
13
  unreachable,
14
+ getSuperblocksDevEnvironmentConfigJson,
14
15
  } from "@superblocksteam/util";
15
16
  import axios, { AxiosError } from "axios";
16
17
  import FormData from "form-data";
@@ -24,6 +25,10 @@ import { signingEnabled } from "./flag.js";
24
25
  import { connectToISocketRPCServer } from "./socket/index.js";
25
26
  import { AgentType } from "./types/index.js";
26
27
  import { getAgentUrl } from "./utils.js";
28
+ import {
29
+ DEFAULT_BRANCH,
30
+ getCurrentGitBranchIfGit,
31
+ } from "./version-control.mjs";
27
32
  import type { StdISocketRPCClient } from "./socket/index.js";
28
33
  import type {
29
34
  ApiWithPb,
@@ -133,6 +138,116 @@ enum ResourceType {
133
138
  BACKEND = "BACKEND",
134
139
  }
135
140
 
141
+ export enum BranchSource {
142
+ DEV_ENVIRONMENT = "dev environment config file",
143
+ COMMAND_LINE = "command line",
144
+ GIT_REPO = "current git repository",
145
+ DEFAULT = "default",
146
+ }
147
+
148
+ export class CurrentBranch {
149
+ public readonly branchName: string;
150
+ public readonly source: BranchSource;
151
+ public readonly sourcePath?: string;
152
+ public readonly userSpecifiedBranch?: string;
153
+
154
+ constructor({
155
+ branchName,
156
+ source,
157
+ sourcePath,
158
+ userSpecifiedBranch,
159
+ }: {
160
+ branchName: string;
161
+ source: BranchSource;
162
+ sourcePath?: string;
163
+ userSpecifiedBranch?: string;
164
+ }) {
165
+ this.branchName = branchName;
166
+ this.source = source;
167
+ this.sourcePath = sourcePath;
168
+ this.userSpecifiedBranch = userSpecifiedBranch;
169
+ }
170
+ isDevEnvironmentWithUserSpecifiedBranch() {
171
+ return (
172
+ this.isDefinedInDevEnvironmentConfig() &&
173
+ this.userSpecifiedBranch !== undefined
174
+ );
175
+ }
176
+ isDefinedInDevEnvironmentConfig(): boolean {
177
+ return this.source === BranchSource.DEV_ENVIRONMENT;
178
+ }
179
+ isDefaultBranch(): boolean {
180
+ return this.source === BranchSource.DEFAULT;
181
+ }
182
+ isGitBranch(): boolean {
183
+ return this.source === BranchSource.GIT_REPO;
184
+ }
185
+ }
186
+
187
+ const DEFAULT_BRANCH_INFO = new CurrentBranch({
188
+ branchName: DEFAULT_BRANCH,
189
+ source: BranchSource.DEFAULT,
190
+ });
191
+
192
+ // TODO (randall): Remove this and the 'index.ts' export when it is no longer needed in the CLI.
193
+ export function getDefaultBranchInfo(): CurrentBranch {
194
+ return DEFAULT_BRANCH_INFO;
195
+ }
196
+
197
+ /**
198
+ * Returns the current branch name, in the following order of precedence:
199
+ * 1. If a branch name is specified in the dev environment config, use that
200
+ * 2. If a branch name is specified, use that
201
+ * 3. If this is in a git repo, use the current branch
202
+ * 4. If this is not in a git repo, use the default branch
203
+ * @param userSpecifiedBranch - The branch name specified by the user
204
+ * @param pathPrefix - The path prefix to use when looking for the dev environment config
205
+ * @returns The current branch information
206
+ */
207
+ export async function getCurrentBranchInfo(
208
+ userSpecifiedBranch?: string,
209
+ pathPrefix?: string,
210
+ ): Promise<CurrentBranch> {
211
+ // First, if there is a dev environment config and a branch name is specified, use that
212
+ try {
213
+ const [devEnvironmentConfig, devEnvConfigPath] =
214
+ await getSuperblocksDevEnvironmentConfigJson(true, pathPrefix);
215
+ if (devEnvironmentConfig.branch) {
216
+ return new CurrentBranch({
217
+ branchName: devEnvironmentConfig.branch,
218
+ source: BranchSource.DEV_ENVIRONMENT,
219
+ sourcePath: devEnvConfigPath,
220
+ userSpecifiedBranch: userSpecifiedBranch,
221
+ });
222
+ }
223
+ } catch {
224
+ // Did not find or could not parse the dev environment config, so continue
225
+ }
226
+ // Second, if the user specified a branch name, use that
227
+ if (userSpecifiedBranch) {
228
+ return new CurrentBranch({
229
+ branchName: userSpecifiedBranch,
230
+ source: BranchSource.COMMAND_LINE,
231
+ userSpecifiedBranch: userSpecifiedBranch,
232
+ });
233
+ }
234
+ // Third, if this is in a git repo, use the current branch
235
+ const currentGitBranch = await getCurrentGitBranchIfGit();
236
+ if (currentGitBranch) {
237
+ return new CurrentBranch({
238
+ branchName: currentGitBranch,
239
+ source: BranchSource.GIT_REPO,
240
+ userSpecifiedBranch: userSpecifiedBranch,
241
+ });
242
+ }
243
+ // Fourth, if this is not in a git repo, use the default branch
244
+ return new CurrentBranch({
245
+ branchName: DEFAULT_BRANCH,
246
+ source: BranchSource.DEFAULT,
247
+ userSpecifiedBranch: userSpecifiedBranch,
248
+ });
249
+ }
250
+
136
251
  export async function fetchApplication({
137
252
  cliVersion,
138
253
  applicationId,
@@ -1,39 +1,113 @@
1
1
  import { createLogger, format, transports } from "winston";
2
+ import type winston from "winston";
2
3
 
3
- export type DevLogger = {
4
- debug: (message: string, ...meta: any[]) => void;
5
- info: (message: string, ...meta: any[]) => void;
6
- warn: (message: string, ...meta: any[]) => void;
7
- error: (message: string, ...meta: any[]) => void;
8
- };
4
+ const activeTransports: winston.transport[] = [];
5
+
6
+ if (process.env.SUPERBLOCKS_IS_CSB === "true") {
7
+ activeTransports.push(
8
+ new transports.File({
9
+ format: format.json(),
10
+ filename: `/tmp/dev-server.log`,
11
+ level: "info",
12
+ }),
13
+ );
14
+ }
15
+
16
+ activeTransports.push(
17
+ new transports.Console({
18
+ format: format.combine(
19
+ format.colorize(),
20
+ format.timestamp({
21
+ format: "HH:mm:ss.SSS",
22
+ }),
23
+ format.printf((props) => {
24
+ const { message, timestamp, error, level } = props;
25
+ const base =
26
+ process.env.SUPERBLOCKS_IS_CSB === "true"
27
+ ? `${timestamp} ${message}`
28
+ : `${message}`;
29
+ if (level === "error") {
30
+ return `${base} ${(error as ErrorMeta["error"]).message} ${(error as ErrorMeta["error"]).stack}`;
31
+ }
32
+ return base;
33
+ }),
34
+ ),
35
+ }),
36
+ );
9
37
 
10
38
  const winstonLogger = createLogger({
11
- level: "info",
39
+ level: "debug",
12
40
  exitOnError: false,
13
41
  format: format.json(),
14
- transports: [
15
- new transports.File({ filename: `/tmp/dev-server.log`, level: "info" }),
16
- new transports.Console({
17
- format: format.combine(
18
- format.printf(({ message }) => `${message}`),
19
- format.colorize({ message: true }),
20
- ),
21
- level: "debug",
22
- }),
23
- ],
42
+ transports: activeTransports,
43
+ });
44
+
45
+ interface ErrorMeta {
46
+ error: {
47
+ kind: string;
48
+ message: string;
49
+ stack?: string;
50
+ };
51
+ }
52
+
53
+ export interface DevLogger {
54
+ log: (...messages: string[]) => void;
55
+ debug: (...messages: string[]) => void;
56
+ info: (...messages: string[]) => void;
57
+ warn: (...messages: string[]) => void;
58
+ error: (message: string, meta?: ErrorMeta) => void;
59
+ }
60
+ const logger: DevLogger = Object.freeze({
61
+ debug: (...messages: string[]) =>
62
+ winstonLogger.debug(
63
+ messages.length > 0 ? messages.join(" ") : (messages[0] as string),
64
+ ),
65
+ log: (...messages: string[]) =>
66
+ winstonLogger.info(
67
+ messages.length > 0 ? messages.join(" ") : (messages[0] as string),
68
+ ),
69
+ info: (...messages: string[]) =>
70
+ winstonLogger.info(
71
+ messages.length > 0 ? messages.join(" ") : (messages[0] as string),
72
+ ),
73
+ warn: (...messages: string[]) =>
74
+ winstonLogger.warn(
75
+ messages.length > 0 ? messages.join(" ") : (messages[0] as string),
76
+ ),
77
+ error: (message: string, meta?: ErrorMeta) =>
78
+ winstonLogger.error(message, meta),
24
79
  });
25
80
 
26
81
  export function getLogger(
27
- loggerOverride?: (message: string) => void,
82
+ loggerOverride?: (...messages: string[]) => void,
28
83
  ): DevLogger {
29
84
  if (!loggerOverride) {
30
- return winstonLogger;
85
+ return logger;
31
86
  }
32
87
 
33
88
  return {
34
89
  debug: loggerOverride,
35
90
  info: loggerOverride,
36
91
  warn: loggerOverride,
37
- error: loggerOverride,
92
+ log: loggerOverride,
93
+ error: loggerOverride as any,
94
+ };
95
+ }
96
+
97
+ export function getErrorMeta(error: unknown): ErrorMeta {
98
+ if (error instanceof Error) {
99
+ return {
100
+ error: {
101
+ kind: error.name,
102
+ message: error.message,
103
+ stack: error.stack,
104
+ },
105
+ };
106
+ }
107
+ return {
108
+ error: {
109
+ kind: "Unknown Error",
110
+ message: JSON.stringify(error),
111
+ },
38
112
  };
39
113
  }
@@ -18,7 +18,8 @@ import {
18
18
  customComponentsPlugin,
19
19
  isCustomComponentsEnabled,
20
20
  } from "./custom-build.mjs";
21
- import { getLogger } from "./dev-logger.mjs";
21
+ import { getErrorMeta, getLogger } from "./dev-logger.mjs";
22
+ import { buildManifestStubPlugin } from "./vite-plugin-build-manifest-stub.mjs";
22
23
  import { ddRumPlugin } from "./vite-plugin-dd-rum.mjs";
23
24
  import { superblocksCdnPlugin } from "./vite-plugin-sb-cdn.mjs";
24
25
  import type { AiService } from "@superblocksteam/vite-plugin-file-sync/ai-service";
@@ -187,13 +188,6 @@ export async function createDevServer({
187
188
  }, viteReject);
188
189
  });
189
190
 
190
- // TODO(code-mode): remove this soon
191
- app.get("/_sb_disconnect", async (_req, res) => {
192
- console.log("GET /_sb_disconnect");
193
- // TODO(code-mode): should this include any validation checks, such as getting a token?
194
- await gracefulShutdown({ logger, serverInitiated: false });
195
- res.send("ok");
196
- });
197
191
  app.post("/_sb_disconnect", async (req, res) => {
198
192
  const { initiatedByEmail, switchingTo } = req.body;
199
193
  // TODO(code-mode): should this include any validation checks, such as getting a token?
@@ -206,7 +200,7 @@ export async function createDevServer({
206
200
  });
207
201
  res.send("ok");
208
202
  } catch (e) {
209
- console.error("Error disconnecting from dev server", e);
203
+ logger.error("Error disconnecting from dev server", getErrorMeta(e));
210
204
  res.status(500).send("Error disconnecting from dev server");
211
205
  }
212
206
  });
@@ -260,7 +254,7 @@ async function startVite({
260
254
  }) {
261
255
  const viteLogger = createLogger();
262
256
  const logger = getLogger(loggerOverride);
263
- viteLogger.info = logger.info;
257
+ viteLogger.info = (msg: string) => logger.info(msg);
264
258
  viteLogger.warn = (msg: string) => {
265
259
  logger.warn(yellow(msg));
266
260
  };
@@ -276,6 +270,8 @@ async function startVite({
276
270
  const isCustomBuildEnabled = await isCustomComponentsEnabled();
277
271
  const customFolder = path.join(root, "custom");
278
272
 
273
+ const draftsFolder = path.join(root, ".superblocks");
274
+
279
275
  const cdnUrl = process.env.SUPERBLOCKS_CDN_URL ?? "http://localhost:4040/cdn";
280
276
 
281
277
  const env = loadEnv(mode, root, "");
@@ -313,7 +309,7 @@ async function startVite({
313
309
  server: {
314
310
  middlewareMode: true,
315
311
  watch: {
316
- ignored: [`${customFolder}/**/*`],
312
+ ignored: [`${customFolder}/**/*`, `${draftsFolder}/**/*`],
317
313
  },
318
314
  hmr: hmrOptions,
319
315
  cors: {
@@ -342,11 +338,13 @@ async function startVite({
342
338
  aiService,
343
339
  httpServer,
344
340
  tracer,
345
- logger,
346
341
  },
347
342
  { isCustomBuildEnabled },
348
343
  ) as Plugin,
349
344
 
345
+ // Add a virtual "stub" module for the build manifest
346
+ buildManifestStubPlugin(),
347
+
350
348
  // for now, only use the CDN locally
351
349
  superblocksCdnPlugin({
352
350
  imports: {
@@ -6,8 +6,13 @@ import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
6
6
  import { Resource } from "@opentelemetry/resources";
7
7
  import { NodeSDK } from "@opentelemetry/sdk-node";
8
8
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
9
+ import {
10
+ OBS_TAG_APPLICATION_ID,
11
+ OBS_TAG_BRANCH,
12
+ } from "@superblocksteam/shared";
9
13
  import { getLocalTokenWithUrl } from "@superblocksteam/util";
10
14
  import packageJson from "../../package.json" with { type: "json" };
15
+ import type { ApplicationConfig } from "../types/index.js";
11
16
  import type { Span } from "@opentelemetry/api";
12
17
 
13
18
  // NOTE: @joeyagreco - this is how the "env" facet is determined in datadog: https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/#3---configure-your-application
@@ -15,6 +20,7 @@ const ATTR_DEPLOYMENT_ENVIRONMENT = "deployment.environment";
15
20
  // NOTE: @joeyagreco - this can be used to determine if we are using mock-csb, staging-csb, prod-csb, etc
16
21
  const ATTR_SUPERBLOCKS_BASE_URL = "superblocks.base_url";
17
22
  const ATTR_SUPERBLOCKS_CLI_TOKEN = "superblocks.cli_token";
23
+ const ATTR_SUPERBLOCKS_IS_CSB = "superblocks.is_csb";
18
24
  let superblocksTracesUrl = undefined;
19
25
  let superblocksHostname = "unknown";
20
26
  let token = "unknown";
@@ -30,45 +36,50 @@ try {
30
36
  console.error("[tracing init] could not determine superblocks base url", e);
31
37
  }
32
38
 
33
- // Initialize the OpenTelemetry SDK
34
- const sdk = new NodeSDK({
35
- resource: Resource.default().merge(
36
- new Resource({
37
- [ATTR_SERVICE_NAME]: "sdk-dev-server",
38
- [ATTR_DEPLOYMENT_ENVIRONMENT]: process.env.SUPERBLOCKS_CLI_ENV,
39
- [ATTR_SUPERBLOCKS_BASE_URL]: superblocksHostname,
40
- [ATTR_SUPERBLOCKS_CLI_TOKEN]: token,
39
+ export const initializeTracingSdk = (applicationConfig?: ApplicationConfig) => {
40
+ // Initialize the OpenTelemetry SDK
41
+ const sdk = new NodeSDK({
42
+ resource: Resource.default().merge(
43
+ new Resource({
44
+ [ATTR_SERVICE_NAME]: "sdk-dev-server",
45
+ [ATTR_DEPLOYMENT_ENVIRONMENT]: process.env.SUPERBLOCKS_CLI_ENV,
46
+ [ATTR_SUPERBLOCKS_BASE_URL]: superblocksHostname,
47
+ [ATTR_SUPERBLOCKS_CLI_TOKEN]: token,
48
+ [OBS_TAG_APPLICATION_ID]: applicationConfig?.id,
49
+ [OBS_TAG_BRANCH]: applicationConfig?.branchName,
50
+ [ATTR_SUPERBLOCKS_IS_CSB]: process.env.SUPERBLOCKS_IS_CSB,
51
+ }),
52
+ ),
53
+ traceExporter: new OTLPTraceExporter({
54
+ url: superblocksTracesUrl, // OTLPTraceExporter defaults to sending traffic to http://localhost:4318/v1/traces
41
55
  }),
42
- ),
43
- traceExporter: new OTLPTraceExporter({
44
- url: superblocksTracesUrl, // OTLPTraceExporter defaults to sending traffic to http://localhost:4318/v1/traces
45
- }),
46
- contextManager: new AsyncLocalStorageContextManager(),
47
- instrumentations: [
48
- // Configure HTTP instrumentation with custom attributes
49
- new HttpInstrumentation({
50
- requestHook: (span: Span, request: any) => {
51
- let resource = "GET";
52
- if (request) {
53
- resource = `${request.method} ${request.url || request.path}`;
54
- }
55
- span.setAttributes({
56
- "resource.name": resource,
57
- });
58
- },
59
- }),
60
- // Configure Express instrumentation
61
- new ExpressInstrumentation({
62
- requestHook: (span: Span, request: any) => {
63
- span.setAttributes({
64
- "resource.name": `${request.method} ${request.url}`,
65
- });
66
- },
67
- }),
68
- ],
69
- });
56
+ contextManager: new AsyncLocalStorageContextManager(),
57
+ instrumentations: [
58
+ // Configure HTTP instrumentation with custom attributes
59
+ new HttpInstrumentation({
60
+ requestHook: (span: Span, request: any) => {
61
+ let resource = "GET";
62
+ if (request) {
63
+ resource = `${request.method} ${request.url || request.path}`;
64
+ }
65
+ span.setAttributes({
66
+ "resource.name": resource,
67
+ });
68
+ },
69
+ }),
70
+ // Configure Express instrumentation
71
+ new ExpressInstrumentation({
72
+ requestHook: (span: Span, request: any) => {
73
+ span.setAttributes({
74
+ "resource.name": `${request.method} ${request.url}`,
75
+ });
76
+ },
77
+ }),
78
+ ],
79
+ });
70
80
 
71
- sdk.start();
81
+ sdk.start();
82
+ };
72
83
 
73
84
  // Get a tracer instance
74
85
  const tracer = trace.getTracer("sdk-dev-server", packageJson.version);