@superblocksteam/sdk 2.0.3-next.174 → 2.0.3-next.176

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 (55) 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.map +1 -1
  6. package/dist/cli-replacement/automatic-upgrades.js +22 -6
  7. package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
  8. package/dist/cli-replacement/dev.d.mts.map +1 -1
  9. package/dist/cli-replacement/dev.mjs +7 -0
  10. package/dist/cli-replacement/dev.mjs.map +1 -1
  11. package/dist/client.d.ts +2 -1
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +7 -5
  14. package/dist/client.js.map +1 -1
  15. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  16. package/dist/dev-utils/dev-server.mjs +30 -8
  17. package/dist/dev-utils/dev-server.mjs.map +1 -1
  18. package/dist/dev-utils/dev-tracer.d.ts.map +1 -1
  19. package/dist/dev-utils/dev-tracer.js +33 -1
  20. package/dist/dev-utils/dev-tracer.js.map +1 -1
  21. package/dist/dev-utils/vite-plugin-react-transform.d.mts.map +1 -1
  22. package/dist/dev-utils/vite-plugin-react-transform.mjs +1 -0
  23. package/dist/dev-utils/vite-plugin-react-transform.mjs.map +1 -1
  24. package/dist/dev-utils/vite-plugin-sb-cdn.d.mts.map +1 -1
  25. package/dist/dev-utils/vite-plugin-sb-cdn.mjs +34 -9
  26. package/dist/dev-utils/vite-plugin-sb-cdn.mjs.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/socket/handlers.d.ts +2 -2
  32. package/dist/socket/handlers.d.ts.map +1 -1
  33. package/dist/socket/handlers.js.map +1 -1
  34. package/dist/socket/index.d.ts +1 -11
  35. package/dist/socket/index.d.ts.map +1 -1
  36. package/dist/socket/index.js +11 -43
  37. package/dist/socket/index.js.map +1 -1
  38. package/dist/vite-plugin-inject-sb-ids-transform.d.mts +15 -0
  39. package/dist/vite-plugin-inject-sb-ids-transform.d.mts.map +1 -0
  40. package/dist/vite-plugin-inject-sb-ids-transform.mjs +86 -0
  41. package/dist/vite-plugin-inject-sb-ids-transform.mjs.map +1 -0
  42. package/package.json +16 -5
  43. package/src/application-build.mts +160 -0
  44. package/src/cli-replacement/automatic-upgrades.ts +30 -9
  45. package/src/cli-replacement/dev.mts +10 -0
  46. package/src/client.ts +13 -4
  47. package/src/dev-utils/dev-server.mts +39 -8
  48. package/src/dev-utils/dev-tracer.ts +35 -1
  49. package/src/dev-utils/vite-plugin-react-transform.mts +1 -0
  50. package/src/dev-utils/vite-plugin-sb-cdn.mts +45 -11
  51. package/src/index.ts +2 -0
  52. package/src/socket/handlers.ts +109 -105
  53. package/src/socket/index.ts +21 -101
  54. package/src/vite-plugin-inject-sb-ids-transform.mts +104 -0
  55. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,86 @@
1
+ import path from "node:path";
2
+ import babelGenerate from "@babel/generator";
3
+ import { parse } from "@babel/parser";
4
+ import { supplementElementIds, generateRootSource, } from "@superblocksteam/vite-plugin-file-sync";
5
+ import { yellow, red } from "colorette";
6
+ import fs from "fs-extra";
7
+ import { createLogger } from "vite";
8
+ import { getLogger } from "./dev-utils/dev-logger.mjs";
9
+ const routesFileBaseName = "routes.json";
10
+ /**
11
+ * Creates a Vite plugin that injects Superblocks IDs into the application.
12
+ * This will primarily be used during builds, as the dev server leverages the
13
+ * file sync manager to inject and keep the IDs up to date.
14
+ *
15
+ * Features:
16
+ * - Injects the root component with the routes data
17
+ * - Injects Superblocks IDs into all components
18
+ *
19
+ * @param root - The root directory of the application
20
+ * @returns A Vite plugin that injects Superblocks IDs into the application's components
21
+ */
22
+ export async function injectSuperblocksIdsPlugin(root) {
23
+ const viteLogger = createLogger();
24
+ const logger = getLogger();
25
+ viteLogger.info = logger.info;
26
+ viteLogger.warn = (msg) => {
27
+ logger.warn(yellow(msg));
28
+ };
29
+ viteLogger.warnOnce = (msg) => {
30
+ logger.warn(yellow(msg));
31
+ };
32
+ viteLogger.error = (msg) => {
33
+ logger.error(red(msg));
34
+ };
35
+ viteLogger.clearScreen = () => { };
36
+ const routes = await getRoutes(root, viteLogger);
37
+ return {
38
+ name: "sb-inject-superblocks-ids",
39
+ enforce: "pre",
40
+ transform(code, id) {
41
+ const relativePath = path.relative(root, id);
42
+ if (relativePath === "root.tsx") {
43
+ const source = generateRootSource(code, routes);
44
+ return {
45
+ code: source,
46
+ map: null,
47
+ };
48
+ }
49
+ else if (id.endsWith(".tsx")) {
50
+ const ast = parse(code, {
51
+ sourceType: "module",
52
+ sourceFilename: id,
53
+ plugins: ["jsx"],
54
+ });
55
+ supplementElementIds({
56
+ fileName: id,
57
+ ast,
58
+ shouldModifyAst: true,
59
+ });
60
+ const result = babelGenerate.default(ast);
61
+ return result.code;
62
+ }
63
+ return code;
64
+ },
65
+ };
66
+ }
67
+ async function getRoutes(root, logger) {
68
+ const routesFile = path.join(root, routesFileBaseName);
69
+ if (!(await fs.pathExists(routesFile))) {
70
+ logger.warn(`routes file not found at expected location: ${routesFile}`);
71
+ return [];
72
+ }
73
+ try {
74
+ const routesData = await fs.readFile(routesFile, "utf-8");
75
+ const routes = JSON.parse(routesData);
76
+ return Object.entries(routes).map(([path, { file }]) => ({
77
+ path,
78
+ component: file,
79
+ }));
80
+ }
81
+ catch (err) {
82
+ logger.error(`error reading routes file: ${routesFile}. error=[${JSON.stringify(err)}]`);
83
+ }
84
+ return [];
85
+ }
86
+ //# sourceMappingURL=vite-plugin-inject-sb-ids-transform.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin-inject-sb-ids-transform.mjs","sourceRoot":"","sources":["../src/vite-plugin-inject-sb-ids-transform.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/sdk",
3
- "version": "2.0.3-next.174",
3
+ "version": "2.0.3-next.176",
4
4
  "type": "module",
5
5
  "description": "Superblocks JS SDK",
6
6
  "homepage": "https://www.superblocks.com",
@@ -16,16 +16,23 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
+ "@babel/core": "7.24.0",
20
+ "@babel/generator": "^7.25.7",
21
+ "@babel/parser": "^7.25.8",
22
+ "@babel/traverse": "^7.25.7",
19
23
  "@opentelemetry/api": "^1.9.0",
20
24
  "@opentelemetry/context-async-hooks": "^2.0.1",
25
+ "@opentelemetry/exporter-trace-otlp-http": "^0.55.0",
21
26
  "@opentelemetry/instrumentation-express": "^0.45.0",
22
27
  "@opentelemetry/instrumentation-http": "^0.55.0",
28
+ "@opentelemetry/resources": "^1.28.0",
23
29
  "@opentelemetry/sdk-node": "^0.55.0",
30
+ "@opentelemetry/semantic-conventions": "^1.28.0",
24
31
  "@rollup/wasm-node": "^4.35.0",
25
- "@superblocksteam/bucketeer-sdk": "0.4.1",
26
- "@superblocksteam/shared": "0.9122.0",
27
- "@superblocksteam/util": "2.0.3-next.174",
28
- "@superblocksteam/vite-plugin-file-sync": "2.0.3-next.174",
32
+ "@superblocksteam/bucketeer-sdk": "0.5.0",
33
+ "@superblocksteam/shared": "0.9160.0",
34
+ "@superblocksteam/util": "2.0.3-next.176",
35
+ "@superblocksteam/vite-plugin-file-sync": "2.0.3-next.176",
29
36
  "@vitejs/plugin-react": "^4.3.4",
30
37
  "axios": "^1.4.0",
31
38
  "chokidar": "^4.0.3",
@@ -53,7 +60,11 @@
53
60
  "yaml": "^2.4.2"
54
61
  },
55
62
  "devDependencies": {
63
+ "@babel/types": "^7.25.8",
56
64
  "@eslint/js": "^9.16.0",
65
+ "@types/babel__core": "^7.20.5",
66
+ "@types/babel__generator": "^7.6.8",
67
+ "@types/babel__traverse": "^7.20.6",
57
68
  "@types/chai": "^4",
58
69
  "@types/chai-as-promised": "^8.0.2",
59
70
  "@types/common-tags": "^1.8.4",
@@ -0,0 +1,160 @@
1
+ import path from "node:path";
2
+ import { injectIndexVitePlugin } from "@superblocksteam/vite-plugin-file-sync/inject-index";
3
+ import react from "@vitejs/plugin-react";
4
+ import { yellow, red } from "colorette";
5
+ import fs from "fs-extra";
6
+ import { build, createLogger } from "vite";
7
+ import tsconfigPaths from "vite-tsconfig-paths";
8
+ import { customComponentsPlugin } from "./dev-utils/custom-build.mjs";
9
+ import { getLogger } from "./dev-utils/dev-logger.mjs";
10
+ import { ddRumPlugin } from "./dev-utils/vite-plugin-dd-rum.mjs";
11
+ import { superblocksCdnPlugin } from "./dev-utils/vite-plugin-sb-cdn.mjs";
12
+ import { injectSuperblocksIdsPlugin } from "./vite-plugin-inject-sb-ids-transform.mjs";
13
+ import type { Plugin } from "vite";
14
+
15
+ export async function buildApplication({
16
+ root,
17
+ dest,
18
+ mode,
19
+ libraryUrl,
20
+ assetsCdnUrl,
21
+ ddClientToken,
22
+ ddApplicationId,
23
+ ddEnv,
24
+ ddVersion,
25
+ }: {
26
+ root: string;
27
+ dest: string;
28
+ mode: string;
29
+ libraryUrl: string;
30
+ assetsCdnUrl?: string;
31
+ ddClientToken?: string;
32
+ ddApplicationId?: string;
33
+ ddEnv?: string;
34
+ ddVersion?: string;
35
+ }) {
36
+ const cwd = process.cwd();
37
+ try {
38
+ // Ensure the root directory exists and change execution context to it
39
+ if (!(await fs.pathExists(root))) {
40
+ throw new Error(`Root directory "${root}" does not exist`);
41
+ }
42
+
43
+ process.chdir(root);
44
+ await buildWithVite({
45
+ root: fs.realpathSync(root),
46
+ dest: fs.realpathSync(dest),
47
+ mode,
48
+ libraryUrl,
49
+ assetsCdnUrl,
50
+ ddClientToken,
51
+ ddApplicationId,
52
+ ddEnv,
53
+ ddVersion,
54
+ });
55
+ } finally {
56
+ // Restore the original working directory before returning
57
+ process.chdir(cwd);
58
+ }
59
+ }
60
+
61
+ async function buildWithVite({
62
+ root,
63
+ dest,
64
+ mode,
65
+ libraryUrl,
66
+ assetsCdnUrl,
67
+ ddClientToken,
68
+ ddApplicationId,
69
+ ddEnv,
70
+ ddVersion,
71
+ }: {
72
+ root: string;
73
+ dest: string;
74
+ mode: string;
75
+ libraryUrl: string;
76
+ assetsCdnUrl?: string;
77
+ ddClientToken?: string;
78
+ ddApplicationId?: string;
79
+ ddEnv?: string;
80
+ ddVersion?: string;
81
+ }) {
82
+ const viteLogger = createLogger();
83
+ const logger = getLogger();
84
+ viteLogger.info = logger.info;
85
+ viteLogger.warn = (msg: string) => {
86
+ logger.warn(yellow(msg));
87
+ };
88
+ viteLogger.warnOnce = (msg: string) => {
89
+ logger.warn(yellow(msg));
90
+ };
91
+ viteLogger.error = (msg: string) => {
92
+ logger.error(red(msg));
93
+ };
94
+
95
+ viteLogger.clearScreen = () => {};
96
+
97
+ const customFolder = path.join(root, "custom");
98
+
99
+ await build({
100
+ root,
101
+ mode,
102
+ appType: "spa",
103
+ resolve: {
104
+ alias: {
105
+ "react-router": "@superblocksteam/library",
106
+ },
107
+ },
108
+ clearScreen: true,
109
+ optimizeDeps: {
110
+ include: ["lodash", "react-is"],
111
+ exclude: [],
112
+ },
113
+ build: {
114
+ outDir: dest,
115
+ emptyOutDir: true,
116
+ write: true,
117
+ commonjsOptions: {
118
+ include: ["react-is"],
119
+ transformMixedEsModules: true,
120
+ },
121
+ rollupOptions: {
122
+ external: [
123
+ `${customFolder}/**/*`,
124
+ "react",
125
+ "react-dom",
126
+ "react/jsx-runtime",
127
+ "react/jsx-dev-runtime",
128
+ ],
129
+ preserveEntrySignatures: "allow-extension",
130
+ output: { format: "esm" },
131
+ },
132
+ },
133
+ logLevel: "info",
134
+ plugins: [
135
+ tsconfigPaths(),
136
+ injectIndexVitePlugin({ assetsCdnUrl, logger }) as Plugin,
137
+ customComponentsPlugin(),
138
+ injectSuperblocksIdsPlugin(root),
139
+ superblocksCdnPlugin({
140
+ imports: {
141
+ "@superblocksteam/library": `${libraryUrl}/index.js`,
142
+ "react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime.mjs",
143
+ "react/jsx-dev-runtime":
144
+ "https://esm.sh/react@18.2.0/jsx-dev-runtime.mjs",
145
+ },
146
+ cssImports: {
147
+ "@superblocksteam/library/index.css": `${libraryUrl}/index.css`,
148
+ },
149
+ }),
150
+ react(),
151
+
152
+ ddRumPlugin({
153
+ clientToken: ddClientToken ?? "",
154
+ applicationId: ddApplicationId ?? "",
155
+ env: ddEnv ?? "prod",
156
+ version: ddVersion ?? "1.0.0",
157
+ }),
158
+ ],
159
+ });
160
+ }
@@ -79,7 +79,11 @@ async function getCurrentCliVersion(): Promise<string | undefined> {
79
79
  );
80
80
  const json = JSON.parse(versionOutput) as Record<string, string>;
81
81
  // Extract version from string like "@superblocksteam/cli/2.0.0-next.1" or "@superblocksteam/cli-ephemeral/2.0.0-SNAPSHOT.1749077365"
82
- return json.cliVersion?.replace(/@superblocksteam\/cli(-ephemeral)?\//, "");
82
+ const version = json.cliVersion?.replace(
83
+ /@superblocksteam\/cli(-ephemeral)?\//,
84
+ "",
85
+ );
86
+ return version;
83
87
  } catch (error) {
84
88
  if (isNativeError(error)) {
85
89
  logger.error(`Error getting CLI version: ${error.message}`);
@@ -258,6 +262,9 @@ export async function checkVersionsAndUpgrade(
258
262
  // Get current versions
259
263
  const currentCliVersion = await getCurrentCliVersion();
260
264
  const currentLibraryInfo = await getCurrentLibraryVersion(pm);
265
+ logger.info(
266
+ `@superblocksteam/cli/${currentCliVersion} ${currentLibraryInfo?.alias} ${currentLibraryInfo?.version}`,
267
+ );
261
268
 
262
269
  // Skip if we're in local development
263
270
  if (
@@ -277,14 +284,28 @@ export async function checkVersionsAndUpgrade(
277
284
  const targetVersions = await getRemoteVersions(config);
278
285
  if (!targetVersions) return;
279
286
 
280
- // Check if CLI needs upgrade
281
- const cliNeedsUpgrade =
282
- targetVersions.cli && gt(targetVersions.cli, currentCliVersion);
283
-
284
- // Check if library needs upgrade
285
- const libraryNeedsUpgrade =
286
- targetVersions.library &&
287
- gt(targetVersions.library, currentLibraryInfo.version);
287
+ let cliNeedsUpgrade: boolean | string;
288
+ let libraryNeedsUpgrade: boolean | string;
289
+ try {
290
+ // If version is latest, then semver can throw an error
291
+ // Check if CLI needs upgrade
292
+ cliNeedsUpgrade =
293
+ targetVersions.cli && gt(targetVersions.cli, currentCliVersion);
294
+
295
+ // Check if library needs upgrade
296
+ libraryNeedsUpgrade =
297
+ targetVersions.library &&
298
+ gt(targetVersions.library, currentLibraryInfo.version);
299
+ } catch (error) {
300
+ console.warn(
301
+ "Error checking versions to upgrade, releasing lock and exiting",
302
+ error,
303
+ );
304
+ await lockService.shutdown({
305
+ serverInitiated: false,
306
+ });
307
+ process.exit(1);
308
+ }
288
309
 
289
310
  if (!cliNeedsUpgrade && !libraryNeedsUpgrade) {
290
311
  return; // Everything is up to date
@@ -2,6 +2,7 @@ import "../dev-utils/dev-tracer.js";
2
2
 
3
3
  import * as child_process from "node:child_process";
4
4
  import * as fsp from "node:fs/promises";
5
+ import path from "node:path";
5
6
  import { promisify } from "node:util";
6
7
  import { maskUnixSignals } from "@superblocksteam/util";
7
8
  import { AiService } from "@superblocksteam/vite-plugin-file-sync/ai-service";
@@ -13,6 +14,7 @@ import { OperationQueue } from "@superblocksteam/vite-plugin-file-sync/operation
13
14
  import { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service";
14
15
  import { green } from "colorette";
15
16
  import { diffJson } from "diff";
17
+ import fs from "fs-extra";
16
18
  import { resolveCommand } from "package-manager-detector";
17
19
  import { detect } from "package-manager-detector/detect";
18
20
 
@@ -115,6 +117,13 @@ export async function dev(options: {
115
117
  applicationConfig,
116
118
  } = options;
117
119
 
120
+ // Add check for node_modules
121
+ if (!fs.existsSync(path.join(cwd, "node_modules"))) {
122
+ throw new Error(
123
+ 'node_modules folder is missing. Please run "npm install" first.',
124
+ );
125
+ }
126
+
118
127
  if (pidfilePath) {
119
128
  await fsp.writeFile(pidfilePath, `${process.pid}\n`);
120
129
  }
@@ -198,6 +207,7 @@ export async function dev(options: {
198
207
  anthropicApiKey: process.env.ANTHROPIC_API_KEY || "",
199
208
  fsOperationQueue,
200
209
  draftInterface: syncService! as DraftInterface,
210
+ tracer,
201
211
  });
202
212
 
203
213
  const isSynced = localContents.hash === serverHash;
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
  }
@@ -84,11 +84,23 @@ export async function createDevServer({
84
84
  viteReject = reject;
85
85
  });
86
86
 
87
- 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
+ }) {
88
98
  try {
89
- if (lockService?.isLocked) {
90
- await lockService.shutdown();
91
- }
99
+ await lockService?.shutdown({
100
+ serverInitiated,
101
+ switchingTo,
102
+ initiatedByEmail,
103
+ });
92
104
  // Cleanup the vite server
93
105
  if (viteServer) {
94
106
  await viteServer?.close();
@@ -118,6 +130,7 @@ export async function createDevServer({
118
130
  credentials: true,
119
131
  }),
120
132
  );
133
+ app.use(express.json());
121
134
 
122
135
  app.use((req, res, next) => {
123
136
  res.setHeader("Cache-Control", "no-store, max-age=0");
@@ -174,11 +187,29 @@ export async function createDevServer({
174
187
  }, viteReject);
175
188
  });
176
189
 
190
+ // TODO(code-mode): remove this soon
177
191
  app.get("/_sb_disconnect", async (_req, res) => {
192
+ console.log("GET /_sb_disconnect");
178
193
  // TODO(code-mode): should this include any validation checks, such as getting a token?
179
- await gracefulShutdown(logger);
194
+ await gracefulShutdown({ logger, serverInitiated: false });
180
195
  res.send("ok");
181
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
+ });
182
213
 
183
214
  app.get("/_sb_status", async (_req, res) => {
184
215
  res.setHeader("Content-Type", "application/json");
@@ -195,17 +226,17 @@ export async function createDevServer({
195
226
 
196
227
  process.on("SIGINT", async () => {
197
228
  logger.info("SIGINT received");
198
- await gracefulShutdown(logger);
229
+ await gracefulShutdown({ logger, serverInitiated: false });
199
230
  });
200
231
 
201
232
  process.on("SIGTERM", async () => {
202
233
  logger.info("SIGTERM received");
203
- await gracefulShutdown(logger);
234
+ await gracefulShutdown({ logger, serverInitiated: false });
204
235
  });
205
236
 
206
237
  process.on("SIGABRT", async () => {
207
238
  logger.info("SIGABRT received");
208
- await gracefulShutdown(logger);
239
+ await gracefulShutdown({ logger, serverInitiated: false });
209
240
  });
210
241
 
211
242
  httpServer = await app.listen(port);
@@ -1,14 +1,48 @@
1
1
  import { trace } from "@opentelemetry/api";
2
2
  import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
3
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
3
4
  import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
4
5
  import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
6
+ import { Resource } from "@opentelemetry/resources";
5
7
  import { NodeSDK } from "@opentelemetry/sdk-node";
8
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
9
+ import { getLocalTokenWithUrl } from "@superblocksteam/util";
6
10
  import packageJson from "../../package.json" with { type: "json" };
7
11
  import type { Span } from "@opentelemetry/api";
8
12
 
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
+ }
32
+
9
33
  // Initialize the OpenTelemetry SDK
10
34
  const sdk = new NodeSDK({
11
- serviceName: "sdk-dev-server",
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
+ }),
12
46
  contextManager: new AsyncLocalStorageContextManager(),
13
47
  instrumentations: [
14
48
  // Configure HTTP instrumentation with custom attributes
@@ -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")) {