@superblocksteam/sdk 2.0.3-next.99 → 2.0.3

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 (67) hide show
  1. package/dist/application-build.d.mts +12 -0
  2. package/dist/application-build.d.mts.map +1 -0
  3. package/dist/application-build.mjs +113 -0
  4. package/dist/application-build.mjs.map +1 -0
  5. package/dist/cli-replacement/automatic-upgrades.d.ts +14 -0
  6. package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -0
  7. package/dist/cli-replacement/automatic-upgrades.js +267 -0
  8. package/dist/cli-replacement/automatic-upgrades.js.map +1 -0
  9. package/dist/cli-replacement/dev.d.mts +3 -6
  10. package/dist/cli-replacement/dev.d.mts.map +1 -1
  11. package/dist/cli-replacement/dev.mjs +80 -3
  12. package/dist/cli-replacement/dev.mjs.map +1 -1
  13. package/dist/client.d.ts +2 -1
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.js +7 -5
  16. package/dist/client.js.map +1 -1
  17. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  18. package/dist/dev-utils/dev-server.mjs +59 -19
  19. package/dist/dev-utils/dev-server.mjs.map +1 -1
  20. package/dist/dev-utils/dev-tracer.d.ts +1 -1
  21. package/dist/dev-utils/dev-tracer.d.ts.map +1 -1
  22. package/dist/dev-utils/dev-tracer.js +66 -24
  23. package/dist/dev-utils/dev-tracer.js.map +1 -1
  24. package/dist/dev-utils/vite-plugin-react-transform.d.mts.map +1 -1
  25. package/dist/dev-utils/vite-plugin-react-transform.mjs +1 -0
  26. package/dist/dev-utils/vite-plugin-react-transform.mjs.map +1 -1
  27. package/dist/dev-utils/vite-plugin-sb-cdn.d.mts.map +1 -1
  28. package/dist/dev-utils/vite-plugin-sb-cdn.mjs +34 -9
  29. package/dist/dev-utils/vite-plugin-sb-cdn.mjs.map +1 -1
  30. package/dist/index.d.ts +4 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/socket/handlers.d.ts +2 -2
  35. package/dist/socket/handlers.d.ts.map +1 -1
  36. package/dist/socket/handlers.js.map +1 -1
  37. package/dist/socket/index.d.ts +1 -11
  38. package/dist/socket/index.d.ts.map +1 -1
  39. package/dist/socket/index.js +11 -43
  40. package/dist/socket/index.js.map +1 -1
  41. package/dist/types/common.d.ts +6 -0
  42. package/dist/types/common.d.ts.map +1 -1
  43. package/dist/types/common.js.map +1 -1
  44. package/dist/vite-plugin-inject-sb-ids-transform.d.mts +15 -0
  45. package/dist/vite-plugin-inject-sb-ids-transform.d.mts.map +1 -0
  46. package/dist/vite-plugin-inject-sb-ids-transform.mjs +86 -0
  47. package/dist/vite-plugin-inject-sb-ids-transform.mjs.map +1 -0
  48. package/package.json +26 -6
  49. package/src/application-build.mts +160 -0
  50. package/src/cli-replacement/automatic-upgrades.ts +363 -0
  51. package/src/cli-replacement/dev.mts +120 -10
  52. package/src/client.ts +13 -4
  53. package/src/dev-utils/dev-server.mts +79 -19
  54. package/src/dev-utils/dev-tracer.ts +69 -24
  55. package/src/dev-utils/vite-plugin-react-transform.mts +1 -0
  56. package/src/dev-utils/vite-plugin-sb-cdn.mts +45 -11
  57. package/src/index.ts +6 -1
  58. package/src/socket/handlers.ts +109 -105
  59. package/src/socket/index.ts +21 -101
  60. package/src/types/common.ts +7 -0
  61. package/src/vite-plugin-inject-sb-ids-transform.mts +104 -0
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/dist/socket/socket.d.ts +0 -61
  64. package/dist/socket/socket.d.ts.map +0 -1
  65. package/dist/socket/socket.js +0 -251
  66. package/dist/socket/socket.js.map +0 -1
  67. package/src/socket/socket.ts +0 -399
@@ -1,5 +1,9 @@
1
1
  import "../dev-utils/dev-tracer.js";
2
+
3
+ import * as child_process from "node:child_process";
2
4
  import * as fsp from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { promisify } from "node:util";
3
7
  import { maskUnixSignals } from "@superblocksteam/util";
4
8
  import { AiService } from "@superblocksteam/vite-plugin-file-sync/ai-service";
5
9
  import {
@@ -9,26 +13,83 @@ import {
9
13
  import { OperationQueue } from "@superblocksteam/vite-plugin-file-sync/operation-queue";
10
14
  import { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service";
11
15
  import { green } from "colorette";
16
+ import { diffJson } from "diff";
17
+ import fs from "fs-extra";
18
+ import { resolveCommand } from "package-manager-detector";
19
+ import { detect } from "package-manager-detector/detect";
20
+
12
21
  import { getLogger } from "../dev-utils/dev-logger.mjs";
13
22
  import { createDevServer } from "../dev-utils/dev-server.mjs";
23
+ import tracer from "../dev-utils/dev-tracer.js";
14
24
  import { SuperblocksSdk } from "../sdk.js";
25
+ import {
26
+ checkVersionsAndUpgrade,
27
+ getCurrentCliVersion,
28
+ getCurrentLibraryVersionWithoutPM,
29
+ } from "./automatic-upgrades.js";
15
30
  import type { DevLogger } from "../dev-utils/dev-logger.mjs";
31
+ import type { ApplicationConfig } from "../types/common.js";
16
32
  import type { DraftInterface } from "@superblocksteam/vite-plugin-file-sync/draft-interface";
17
33
 
18
- export interface ApplicationConfig {
19
- superblocksBaseUrl: string;
20
- token: string;
21
- id: string;
22
- branchName: string;
23
- }
34
+ const exec = promisify(child_process.exec);
24
35
 
25
- const passErrorToVSCode = (message: string, logger: DevLogger) => {
36
+ const passErrorToVSCode = (message: string | undefined, logger: DevLogger) => {
26
37
  if (message) {
27
38
  // Prefixing with `clierr:` will make the VS code extension capture this message and show it to the user.
28
39
  logger.error(`clierr: ${JSON.stringify({ message })}`);
29
40
  }
30
41
  };
31
42
 
43
+ async function readPkgJson(cwd: string) {
44
+ try {
45
+ const { readPackage } = await import("read-pkg");
46
+ return await readPackage({ cwd });
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ async function installPackages(cwd: string, logger: DevLogger) {
53
+ try {
54
+ const pm = await detect({
55
+ strategies: [
56
+ "packageManager-field",
57
+ "lockfile",
58
+ "install-metadata",
59
+ "devEngines-field",
60
+ ],
61
+ cwd,
62
+ });
63
+
64
+ if (!pm) {
65
+ logger.warn(
66
+ "Could not detect package manager, skipping package installation",
67
+ );
68
+ return;
69
+ }
70
+
71
+ const installCommand = resolveCommand(pm.agent, "install", []);
72
+
73
+ if (!installCommand) {
74
+ logger.warn(
75
+ `Could not determine install command for ${pm.agent}, skipping package installation`,
76
+ );
77
+ return;
78
+ }
79
+
80
+ const { command, args } = installCommand;
81
+ logger.info(
82
+ `Running ${command} ${args.join(" ")} to install dependencies…`,
83
+ );
84
+
85
+ await exec(`${command} ${args.join(" ")}`, { cwd });
86
+ logger.info("Package installation completed successfully");
87
+ } catch (error) {
88
+ logger.error(`Error during package installation: ${error}`);
89
+ throw error;
90
+ }
91
+ }
92
+
32
93
  export async function dev(options: {
33
94
  /* cwd is required */
34
95
  cwd: string;
@@ -42,9 +103,15 @@ export async function dev(options: {
42
103
  skipSync?: boolean;
43
104
 
44
105
  applicationConfig?: ApplicationConfig;
106
+
107
+ /* For debugging purposes to get location of the cli */
108
+ superblocksPath?: string;
45
109
  /* For redirecting output, like when running outside of the CLI */
46
110
  logger?: (message: string) => void;
47
111
 
112
+ /* For a child process when restarting the dev server for automatic upgrades */
113
+ skipAutoUpgrade?: boolean;
114
+
48
115
  /* To cancel the dev server */
49
116
  signal?: AbortSignal;
50
117
  }) {
@@ -58,8 +125,22 @@ export async function dev(options: {
58
125
  uploadFirst,
59
126
  skipSync,
60
127
  applicationConfig,
128
+ skipAutoUpgrade,
61
129
  } = options;
62
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
+ );
136
+
137
+ // Add check for node_modules
138
+ if (!fs.existsSync(path.join(cwd, "node_modules"))) {
139
+ throw new Error(
140
+ 'node_modules folder is missing. Please run "npm install" first.',
141
+ );
142
+ }
143
+
63
144
  if (pidfilePath) {
64
145
  await fsp.writeFile(pidfilePath, `${process.pid}\n`);
65
146
  }
@@ -81,6 +162,7 @@ export async function dev(options: {
81
162
  process.env.SUPERBLOCKS_IS_CSB === "true"
82
163
  ? LockType.CSB
83
164
  : LockType.LOCAL,
165
+ tracer,
84
166
  });
85
167
  syncService = new SyncService({
86
168
  authToken: applicationConfig.token,
@@ -90,6 +172,7 @@ export async function dev(options: {
90
172
  branchName: applicationConfig.branchName,
91
173
  fsOperationQueue,
92
174
  lockService,
175
+ tracer,
93
176
  });
94
177
 
95
178
  logger.info("Checking if local files are synced with the server");
@@ -105,12 +188,21 @@ export async function dev(options: {
105
188
  if (lockService) {
106
189
  try {
107
190
  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
+ }
108
200
  } catch (error) {
201
+ logger.error("Failed to acquire lock on application", error);
109
202
  passErrorToVSCode(
110
- (error as { context: { message: string } }).context.message,
203
+ (error as { context?: { message: string } }).context?.message,
111
204
  logger,
112
205
  );
113
- logger.error("Failed to acquire lock on application", error);
114
206
  throw new Error(`Failed to acquire lock on application: ${error}`);
115
207
  }
116
208
  }
@@ -133,6 +225,7 @@ export async function dev(options: {
133
225
  anthropicApiKey: process.env.ANTHROPIC_API_KEY || "",
134
226
  fsOperationQueue,
135
227
  draftInterface: syncService! as DraftInterface,
228
+ tracer,
136
229
  });
137
230
 
138
231
  const isSynced = localContents.hash === serverHash;
@@ -153,12 +246,29 @@ export async function dev(options: {
153
246
  throw new Error("Choose either --download-first or --upload-first");
154
247
  }
155
248
 
156
- // TODO(code-mode): pre-process package.json to handle version upgrades
157
249
  if (downloadFirst) {
158
250
  logger.info(
159
251
  "Starting directory sync from server before starting the local server",
160
252
  );
253
+
254
+ // Read package.json before download
255
+ const packageJsonBefore = await readPkgJson(cwd);
256
+
161
257
  await syncService!.downloadDirectory();
258
+
259
+ // Read package.json after download and compare
260
+ const packageJsonAfter = await readPkgJson(cwd);
261
+
262
+ // Check if package.json changed
263
+ if (packageJsonBefore && packageJsonAfter) {
264
+ const diff = diffJson(packageJsonBefore, packageJsonAfter);
265
+ const hasChanges = diff.some((part) => part.added || part.removed);
266
+
267
+ if (hasChanges) {
268
+ logger.info("package.json has changed, installing packages…");
269
+ await installPackages(cwd, logger);
270
+ }
271
+ }
162
272
  } else if (uploadFirst) {
163
273
  logger.info("Uploading local files to server before starting");
164
274
  await syncService!.uploadDirectory();
package/src/client.ts CHANGED
@@ -1,4 +1,5 @@
1
- import * as fs from "fs";
1
+ import * as fs from "node:fs";
2
+ import path from "node:path";
2
3
  import { Bucketeer, FileDescriptor } from "@superblocksteam/bucketeer-sdk";
3
4
  import { ExportViewMode } from "@superblocksteam/shared";
4
5
  import {
@@ -1269,13 +1270,15 @@ export async function uploadApplication({
1269
1270
  scopedJwt,
1270
1271
  url,
1271
1272
  cliVersion,
1273
+ appRoot,
1272
1274
  }: {
1273
1275
  files: string[];
1274
1276
  scopedJwt: string;
1275
1277
  url: string;
1276
1278
  cliVersion: string;
1279
+ appRoot?: string;
1277
1280
  }) {
1278
- const fds = filesToFileDescriptors(files);
1281
+ const fds = filesToFileDescriptors(files, appRoot);
1279
1282
  const bucketeer = new Bucketeer({
1280
1283
  token: scopedJwt,
1281
1284
  baseUrl: url,
@@ -1285,9 +1288,15 @@ export async function uploadApplication({
1285
1288
  await bucketeer.uploadApplication(fds);
1286
1289
  }
1287
1290
 
1288
- function filesToFileDescriptors(files: string[]) {
1291
+ function filesToFileDescriptors(files: string[], appRoot?: string) {
1289
1292
  const fds = files.map((file) => {
1290
- return new FileDescriptor(file, fs.createReadStream(file));
1293
+ const relativePath = appRoot ? path.relative(appRoot, file) : undefined;
1294
+ return new FileDescriptor(
1295
+ file,
1296
+ fs.createReadStream(file),
1297
+ undefined,
1298
+ relativePath,
1299
+ );
1291
1300
  });
1292
1301
  return fds;
1293
1302
  }
@@ -3,11 +3,13 @@
3
3
  import tracer from "./dev-tracer.js";
4
4
 
5
5
  import net from "node:net";
6
+ import os from "node:os";
6
7
  import path from "node:path";
7
8
  import { getSuperblocksResourceConfigIfExists } from "@superblocksteam/util";
8
9
  import { fileSyncVitePlugin } from "@superblocksteam/vite-plugin-file-sync";
9
10
  import react from "@vitejs/plugin-react";
10
11
  import { red, yellow } from "colorette";
12
+ import cors from "cors";
11
13
  import express from "express";
12
14
  import { createLogger, createServer, loadEnv } from "vite";
13
15
  import tsconfigPaths from "vite-tsconfig-paths";
@@ -24,8 +26,9 @@ import type { LockService } from "@superblocksteam/vite-plugin-file-sync/lock-se
24
26
  import type { OperationQueue } from "@superblocksteam/vite-plugin-file-sync/operation-queue";
25
27
  import type { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service";
26
28
  import type http from "node:http";
29
+ import type { HmrOptions, Plugin } from "vite";
27
30
  import type { ViteDevServer } from "vite";
28
- import type { Plugin } from "vite";
31
+
29
32
  interface CreateDevServerOptions {
30
33
  root: string;
31
34
  mode: "development" | "production";
@@ -81,12 +84,23 @@ export async function createDevServer({
81
84
  viteReject = reject;
82
85
  });
83
86
 
84
- async function gracefulShutdown(logger: ReturnType<typeof getLogger>) {
87
+ async function gracefulShutdown({
88
+ logger,
89
+ serverInitiated,
90
+ switchingTo,
91
+ initiatedByEmail,
92
+ }: {
93
+ logger: ReturnType<typeof getLogger>;
94
+ serverInitiated: boolean;
95
+ switchingTo?: "local" | "cloud" | "none";
96
+ initiatedByEmail?: string;
97
+ }) {
85
98
  try {
86
- if (lockService?.isLocked) {
87
- await syncService?.uploadDirectoryNowIfNeeded(); // Perform a final sync before disconnecting
88
- await lockService.releaseLock(); // Release the lock before closing the vite server
89
- }
99
+ await lockService?.shutdown({
100
+ serverInitiated,
101
+ switchingTo,
102
+ initiatedByEmail,
103
+ });
90
104
  // Cleanup the vite server
91
105
  if (viteServer) {
92
106
  await viteServer?.close();
@@ -110,8 +124,16 @@ export async function createDevServer({
110
124
  }
111
125
  }
112
126
 
127
+ app.use(
128
+ cors({
129
+ origin: true,
130
+ credentials: true,
131
+ }),
132
+ );
133
+ app.use(express.json());
134
+
113
135
  app.use((req, res, next) => {
114
- res.setHeader("Access-Control-Allow-Origin", "*");
136
+ res.setHeader("Cache-Control", "no-store, max-age=0");
115
137
 
116
138
  if (
117
139
  req.url === "/_sb_connect" ||
@@ -135,14 +157,12 @@ export async function createDevServer({
135
157
 
136
158
  app.get("/_sb_health", async (_req, res) => {
137
159
  res.setHeader("Content-Type", "application/json");
138
- res.setHeader("Access-Control-Allow-Origin", "*");
139
160
  res.send(JSON.stringify(healthResponse));
140
161
  });
141
162
 
142
163
  // Attach the vite server to the express app
143
164
  app.get("/_sb_connect", async (_req, res) => {
144
165
  res.setHeader("Content-Type", "application/json");
145
- res.setHeader("Access-Control-Allow-Origin", "*");
146
166
  if (isViteServerInitialized) {
147
167
  res.send(JSON.stringify(healthResponse));
148
168
  return;
@@ -150,6 +170,7 @@ export async function createDevServer({
150
170
  isViteServerInitialized = true;
151
171
  // TODO(code-mode): should this include any validation checks, such as getting a token?
152
172
  startVite({
173
+ port,
153
174
  app,
154
175
  root,
155
176
  mode,
@@ -166,15 +187,32 @@ export async function createDevServer({
166
187
  }, viteReject);
167
188
  });
168
189
 
190
+ // TODO(code-mode): remove this soon
169
191
  app.get("/_sb_disconnect", async (_req, res) => {
192
+ console.log("GET /_sb_disconnect");
170
193
  // TODO(code-mode): should this include any validation checks, such as getting a token?
171
- await gracefulShutdown(logger);
194
+ await gracefulShutdown({ logger, serverInitiated: false });
172
195
  res.send("ok");
173
196
  });
197
+ app.post("/_sb_disconnect", async (req, res) => {
198
+ const { initiatedByEmail, switchingTo } = req.body;
199
+ // TODO(code-mode): should this include any validation checks, such as getting a token?
200
+ try {
201
+ await gracefulShutdown({
202
+ logger,
203
+ serverInitiated: true,
204
+ switchingTo,
205
+ initiatedByEmail: initiatedByEmail as string | undefined,
206
+ });
207
+ res.send("ok");
208
+ } catch (e) {
209
+ console.error("Error disconnecting from dev server", e);
210
+ res.status(500).send("Error disconnecting from dev server");
211
+ }
212
+ });
174
213
 
175
214
  app.get("/_sb_status", async (_req, res) => {
176
215
  res.setHeader("Content-Type", "application/json");
177
- res.setHeader("Access-Control-Allow-Origin", "*");
178
216
  if (lockService?.isLocked) {
179
217
  res.send({
180
218
  connectedUsers: lockService.connectedUsers,
@@ -188,17 +226,17 @@ export async function createDevServer({
188
226
 
189
227
  process.on("SIGINT", async () => {
190
228
  logger.info("SIGINT received");
191
- await gracefulShutdown(logger);
229
+ await gracefulShutdown({ logger, serverInitiated: false });
192
230
  });
193
231
 
194
232
  process.on("SIGTERM", async () => {
195
233
  logger.info("SIGTERM received");
196
- await gracefulShutdown(logger);
234
+ await gracefulShutdown({ logger, serverInitiated: false });
197
235
  });
198
236
 
199
237
  process.on("SIGABRT", async () => {
200
238
  logger.info("SIGABRT received");
201
- await gracefulShutdown(logger);
239
+ await gracefulShutdown({ logger, serverInitiated: false });
202
240
  });
203
241
 
204
242
  httpServer = await app.listen(port);
@@ -210,6 +248,7 @@ async function startVite({
210
248
  httpServer,
211
249
  root,
212
250
  mode,
251
+ port,
213
252
  fsOperationQueue,
214
253
  syncService,
215
254
  lockService,
@@ -243,6 +282,19 @@ async function startVite({
243
282
 
244
283
  const hmrPort = await getFreePort();
245
284
 
285
+ const hmrOptions: HmrOptions = {
286
+ port: hmrPort,
287
+ clientPort: port,
288
+ server: httpServer,
289
+ };
290
+
291
+ // https://codesandbox.io/docs/learn/environment/preview-urls#using-environment-variables
292
+ if (process.env.CSB_BASE_PREVIEW_HOST) {
293
+ const hmrHost = `${os.hostname()}-${port}.${process.env.CSB_BASE_PREVIEW_HOST}`;
294
+ hmrOptions.host = hmrHost;
295
+ hmrOptions.clientPort = 443; // tells client to use default ssl port
296
+ }
297
+
246
298
  const viteServer = await createServer({
247
299
  root,
248
300
  mode,
@@ -260,12 +312,13 @@ async function startVite({
260
312
  // appType: "custom",
261
313
  server: {
262
314
  middlewareMode: true,
263
- // port: port,
264
315
  watch: {
265
316
  ignored: [`${customFolder}/**/*`],
266
317
  },
267
- hmr: {
268
- port: hmrPort,
318
+ hmr: hmrOptions,
319
+ cors: {
320
+ origin: true,
321
+ credentials: true,
269
322
  },
270
323
  },
271
324
  build: {
@@ -297,10 +350,13 @@ async function startVite({
297
350
  // for now, only use the CDN locally
298
351
  superblocksCdnPlugin({
299
352
  imports: {
300
- "@superblocksteam/library": `${cdnUrl}/index.js`,
353
+ "@superblocksteam/library": getValidFileUrl(cdnUrl, "index.js"),
301
354
  },
302
355
  cssImports: {
303
- "@superblocksteam/library/index.css": `${cdnUrl}/index.css`,
356
+ "@superblocksteam/library/index.css": getValidFileUrl(
357
+ cdnUrl,
358
+ "index.css",
359
+ ),
304
360
  },
305
361
  }),
306
362
 
@@ -340,3 +396,7 @@ function getFreePort() {
340
396
  });
341
397
  });
342
398
  }
399
+
400
+ function getValidFileUrl(url: string, file: string): string {
401
+ return new URL(file, url.endsWith("/") ? url : url + "/").href;
402
+ }
@@ -1,31 +1,76 @@
1
- import tracer from "dd-trace";
1
+ import { trace } from "@opentelemetry/api";
2
+ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
3
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
4
+ import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
5
+ import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
6
+ import { Resource } from "@opentelemetry/resources";
7
+ import { NodeSDK } from "@opentelemetry/sdk-node";
8
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
9
+ import { getLocalTokenWithUrl } from "@superblocksteam/util";
2
10
  import packageJson from "../../package.json" with { type: "json" };
11
+ import type { Span } from "@opentelemetry/api";
3
12
 
4
- tracer.init({
5
- service: "sdk-dev-server",
6
- version: packageJson.version,
7
- env: "prod",
8
- logInjection: true,
9
- });
13
+ // NOTE: @joeyagreco - this is how the "env" facet is determined in datadog: https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/#3---configure-your-application
14
+ const ATTR_DEPLOYMENT_ENVIRONMENT = "deployment.environment";
15
+ // NOTE: @joeyagreco - this can be used to determine if we are using mock-csb, staging-csb, prod-csb, etc
16
+ const ATTR_SUPERBLOCKS_BASE_URL = "superblocks.base_url";
17
+ const ATTR_SUPERBLOCKS_CLI_TOKEN = "superblocks.cli_token";
18
+ let superblocksTracesUrl = undefined;
19
+ let superblocksHostname = "unknown";
20
+ let token = "unknown";
21
+ try {
22
+ const tokenWithUrl = await getLocalTokenWithUrl();
23
+ const superblocksBaseUrl = new URL(tokenWithUrl.superblocksBaseUrl);
24
+ superblocksTracesUrl = superblocksBaseUrl.origin + "/api/v1/traces";
25
+ superblocksHostname = superblocksBaseUrl.hostname;
26
+ if ("token" in tokenWithUrl) {
27
+ token = tokenWithUrl.token.substring(0, 8);
28
+ }
29
+ } catch (e) {
30
+ console.error("[tracing init] could not determine superblocks base url", e);
31
+ }
10
32
 
11
- tracer.use("http", {
12
- hooks: {
13
- request: (span, req) => {
14
- let resource = "GET";
15
- if (req) {
16
- resource = `${req?.method} ${"path" in req ? req?.path : req?.url}`;
17
- }
18
- span?.setTag("resource.name", resource);
19
- },
20
- },
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,
41
+ }),
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
+ ],
21
69
  });
22
70
 
23
- tracer.use("express", {
24
- hooks: {
25
- request: (span, req) => {
26
- span?.setTag("resource.name", `${req?.method} ${req?.url}`);
27
- },
28
- },
29
- });
71
+ sdk.start();
72
+
73
+ // Get a tracer instance
74
+ const tracer = trace.getTracer("sdk-dev-server", packageJson.version);
30
75
 
31
76
  export default tracer;
@@ -11,6 +11,7 @@ export function reactTransformPlugin(): Plugin {
11
11
  return {
12
12
  name: "vite-plugin-react-transform",
13
13
  enforce: "pre", // Run before other plugins
14
+ apply: "serve",
14
15
  async transform(code, id) {
15
16
  // Check for React modules
16
17
  if (id.includes("node_modules/.vite/deps/react.js")) {