@sentry/junior 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -2,11 +2,6 @@
2
2
 
3
3
  `@sentry/junior` is a Slack bot package for Next.js apps.
4
4
 
5
- If you are contributing to this monorepo, use the root docs:
6
-
7
- - `README.md` for general usage
8
- - `CONTRIBUTING.md` for development workflows
9
-
10
5
  ## Install
11
6
 
12
7
  ```bash
@@ -14,18 +9,7 @@ pnpm add @sentry/junior
14
9
  pnpm add next react react-dom @sentry/nextjs
15
10
  ```
16
11
 
17
- ## Required App Files
18
-
19
- Add these files under `app/`:
20
-
21
- ```text
22
- app/SOUL.md
23
- app/ABOUT.md
24
- app/skills/
25
- app/plugins/ (optional)
26
- ```
27
-
28
- ## Next.js Integration
12
+ ## Quick usage
29
13
 
30
14
  `app/api/[...path]/route.js`:
31
15
 
@@ -48,44 +32,11 @@ import { withJunior } from "@sentry/junior/config";
48
32
  export default withJunior();
49
33
  ```
50
34
 
51
- `instrumentation.js`:
52
-
53
- ```js
54
- export { register, onRequestError } from "@sentry/junior/instrumentation";
55
- ```
56
-
57
- If your app does not already include a root layout:
35
+ ## Full docs
58
36
 
59
- `app/layout.js`:
37
+ Canonical docs: **https://junior.sentry.dev/**
60
38
 
61
- ```js
62
- export { default } from "@sentry/junior/app/layout";
63
- ```
64
-
65
- ## Scaffold a New Bot
66
-
67
- ```bash
68
- npx junior init my-bot
69
- cd my-bot
70
- pnpm install
71
- pnpm dev
72
- ```
73
-
74
- ## Vercel Queue Trigger
75
-
76
- Add this `vercel.json` function trigger:
77
-
78
- ```json
79
- {
80
- "functions": {
81
- "app/api/queue/callback/route.js": {
82
- "experimentalTriggers": [
83
- {
84
- "type": "queue/v2beta",
85
- "topic": "junior-thread-message"
86
- }
87
- ]
88
- }
89
- }
90
- }
91
- ```
39
+ - Quickstart: https://junior.sentry.dev/start-here/quickstart/
40
+ - Deployment: https://junior.sentry.dev/start-here/deploy/
41
+ - Plugin setup: https://junior.sentry.dev/extend/plugins-overview/
42
+ - API reference: https://junior.sentry.dev/reference/api/
package/bin/junior.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "node:fs";
4
3
  import path from "node:path";
5
4
  import { parseArgs } from "node:util";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
6
 
7
7
  const { positionals } = parseArgs({
8
8
  allowPositionals: true,
@@ -11,138 +11,48 @@ const { positionals } = parseArgs({
11
11
 
12
12
  const command = positionals[0];
13
13
 
14
- function writeWrapperFiles(targetDir) {
15
- // app/api/[...path]/route.js
16
- const routeDir = path.join(targetDir, "app", "api", "[...path]");
17
- fs.mkdirSync(routeDir, { recursive: true });
18
- fs.writeFileSync(
19
- path.join(routeDir, "route.js"),
20
- 'export { GET, POST } from "@sentry/junior/handler";\n' +
21
- 'export const runtime = "nodejs";\n'
22
- );
23
-
24
- // app/api/queue/callback/route.js
25
- const queueRouteDir = path.join(targetDir, "app", "api", "queue", "callback");
26
- fs.mkdirSync(queueRouteDir, { recursive: true });
27
- fs.writeFileSync(
28
- path.join(queueRouteDir, "route.js"),
29
- 'export { POST } from "@sentry/junior/handlers/queue-callback";\n' +
30
- 'export const runtime = "nodejs";\n'
31
- );
32
-
33
- // app/layout.js
34
- fs.mkdirSync(path.join(targetDir, "app"), { recursive: true });
35
- fs.writeFileSync(
36
- path.join(targetDir, "app", "layout.js"),
37
- 'export { default } from "@sentry/junior/app/layout";\n'
38
- );
14
+ async function loadCliFunction(moduleName, exportName, unavailableMessage) {
15
+ const currentFile = fileURLToPath(import.meta.url);
16
+ const modulePath = path.join(path.dirname(currentFile), "..", "dist", "cli", `${moduleName}.js`);
17
+ const moduleUrl = pathToFileURL(modulePath).href;
18
+ const loadedModule = await import(moduleUrl);
19
+ if (typeof loadedModule[exportName] !== "function") {
20
+ throw new Error(unavailableMessage);
21
+ }
22
+ return loadedModule[exportName];
23
+ }
39
24
 
40
- // next.config.mjs
41
- fs.writeFileSync(
42
- path.join(targetDir, "next.config.mjs"),
43
- 'import { withJunior } from "@sentry/junior/config";\n' +
44
- 'export default withJunior();\n'
25
+ async function runSnapshotCreate() {
26
+ const runSnapshotCreateFn = await loadCliFunction(
27
+ "snapshot-warmup",
28
+ "runSnapshotCreate",
29
+ "Snapshot create module is unavailable; reinstall @sentry/junior and retry."
45
30
  );
31
+ await runSnapshotCreateFn();
32
+ }
46
33
 
47
- // instrumentation.js
48
- fs.writeFileSync(
49
- path.join(targetDir, "instrumentation.js"),
50
- 'export { register, onRequestError } from "@sentry/junior/instrumentation";\n'
51
- );
34
+ async function runInit(dir) {
35
+ const runInitFn = await loadCliFunction("init", "runInit", "Init module is unavailable; reinstall @sentry/junior and retry.");
36
+ await runInitFn(dir);
52
37
  }
53
38
 
54
- // ---------------------------------------------------------------------------
55
- // junior init <dir>
56
- // ---------------------------------------------------------------------------
57
- if (command === "init") {
58
- const dir = positionals[1];
59
- if (!dir) {
60
- console.error("usage: junior init <dir>");
61
- process.exit(1);
39
+ async function main() {
40
+ const runCli = await loadCliFunction(
41
+ "run",
42
+ "runCli",
43
+ "CLI dispatcher module is unavailable; reinstall @sentry/junior and retry."
44
+ );
45
+ const exitCode = await runCli(positionals, {
46
+ runInit,
47
+ runSnapshotCreate
48
+ });
49
+ if (exitCode !== 0) {
50
+ process.exit(exitCode);
62
51
  }
63
-
64
- const target = path.resolve(dir);
65
- fs.mkdirSync(target, { recursive: true });
66
-
67
- const name = path.basename(target);
68
-
69
- // package.json
70
- const pkg = {
71
- name,
72
- version: "0.1.0",
73
- private: true,
74
- type: "module",
75
- scripts: {
76
- dev: "next dev",
77
- build: "next build",
78
- start: "next start"
79
- },
80
- dependencies: {
81
- "@sentry/junior": "latest",
82
- next: "^16.0.0",
83
- react: "^19.0.0",
84
- "react-dom": "^19.0.0",
85
- "@sentry/nextjs": "^10.0.0"
86
- }
87
- };
88
- fs.writeFileSync(
89
- path.join(target, "package.json"),
90
- JSON.stringify(pkg, null, 2) + "\n"
91
- );
92
-
93
- // app/data/SOUL.md
94
- const dataDir = path.join(target, "app", "data");
95
- fs.mkdirSync(dataDir, { recursive: true });
96
- fs.writeFileSync(
97
- path.join(dataDir, "SOUL.md"),
98
- `# ${name}\n\nYou are ${name}, a helpful assistant.\n`
99
- );
100
-
101
- // app/skills/
102
- const skillsDir = path.join(target, "app", "skills");
103
- fs.mkdirSync(skillsDir, { recursive: true });
104
- fs.writeFileSync(path.join(skillsDir, ".gitkeep"), "");
105
-
106
- // app/plugins/
107
- const pluginsDir = path.join(target, "app", "plugins");
108
- fs.mkdirSync(pluginsDir, { recursive: true });
109
- fs.writeFileSync(path.join(pluginsDir, ".gitkeep"), "");
110
-
111
- // .gitignore
112
- fs.writeFileSync(
113
- path.join(target, ".gitignore"),
114
- [
115
- "node_modules/",
116
- ".next/",
117
- ".env",
118
- ".env.local",
119
- ""
120
- ].join("\n")
121
- );
122
-
123
- // .env.example
124
- fs.writeFileSync(
125
- path.join(target, ".env.example"),
126
- [
127
- "SLACK_BOT_TOKEN=",
128
- "SLACK_SIGNING_SECRET=",
129
- "JUNIOR_BOT_NAME=",
130
- "AI_MODEL=",
131
- "AI_FAST_MODEL=",
132
- "REDIS_URL=",
133
- "NEXT_PUBLIC_SENTRY_DSN=",
134
- ""
135
- ].join("\n")
136
- );
137
-
138
- writeWrapperFiles(target);
139
-
140
- console.log(`Created ${name} at ${target}`);
141
- console.log();
142
- console.log(` cd ${dir} && pnpm install && pnpm dev`);
143
- console.log();
144
- process.exit(0);
145
52
  }
146
53
 
147
- console.error("usage: junior init <dir>");
148
- process.exit(1);
54
+ main().catch((error) => {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ console.error(`junior command failed: ${message}`);
57
+ process.exit(1);
58
+ });
@@ -1,6 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
 
4
+ /**
5
+ * Minimal root layout export for apps that do not provide one yet.
6
+ */
4
7
  declare function RootLayout({ children }: {
5
8
  children: ReactNode;
6
9
  }): react_jsx_runtime.JSX.Element;
@@ -0,0 +1,16 @@
1
+ import {
2
+ appSlackRuntime,
3
+ bot,
4
+ createNormalizingStream,
5
+ resetBotDepsForTests,
6
+ setBotDepsForTests
7
+ } from "./chunk-RXNMJQPY.js";
8
+ import "./chunk-QHDDCUTN.js";
9
+ import "./chunk-PY4AI2GZ.js";
10
+ export {
11
+ appSlackRuntime,
12
+ bot,
13
+ createNormalizingStream,
14
+ resetBotDepsForTests,
15
+ setBotDepsForTests
16
+ };
@@ -6,13 +6,13 @@ import {
6
6
  setSpanStatus,
7
7
  withContext,
8
8
  withSpan
9
- } from "./chunk-BBOVH5RF.js";
9
+ } from "./chunk-PY4AI2GZ.js";
10
10
 
11
11
  // src/handlers/webhooks.ts
12
12
  import { after } from "next/server";
13
13
  import * as Sentry from "@sentry/nextjs";
14
14
  async function loadBot() {
15
- const { bot } = await import("./bot-DLML4Z7F.js");
15
+ const { bot } = await import("./bot-T73QBC4J.js");
16
16
  return bot;
17
17
  }
18
18
  async function POST(request, context) {
@@ -225,13 +225,21 @@ function emitSentry(level, body, attributes) {
225
225
  loggerFn(body, attributes);
226
226
  return;
227
227
  }
228
- Sentry.withScope((scope) => {
229
- for (const [key, value] of Object.entries(attributes)) {
230
- scope.setExtra(key, value);
231
- }
232
- const sentryLevel = level === "warn" ? "warning" : level;
233
- Sentry.captureMessage(body, sentryLevel);
234
- });
228
+ const sentryWithScope = sentry.withScope;
229
+ const sentryCaptureMessage = sentry.captureMessage;
230
+ const sentryLevel = level === "warn" ? "warning" : level;
231
+ if (typeof sentryWithScope === "function" && typeof sentryCaptureMessage === "function") {
232
+ sentryWithScope((scope) => {
233
+ for (const [key, value] of Object.entries(attributes)) {
234
+ scope.setExtra(key, value);
235
+ }
236
+ sentryCaptureMessage(body, sentryLevel);
237
+ });
238
+ return;
239
+ }
240
+ if (typeof sentryCaptureMessage === "function") {
241
+ sentryCaptureMessage(body, sentryLevel);
242
+ }
235
243
  }
236
244
  function formatConsoleLevel(level) {
237
245
  if (level === "debug") return "DBG";
@@ -353,12 +361,22 @@ var log = {
353
361
  "exception.message": normalizedError.message,
354
362
  "exception.stacktrace": normalizedError.stack
355
363
  }, body ?? normalizedError.message);
356
- Sentry.withScope((scope) => {
357
- for (const [key, value] of Object.entries(mergeAttributes(contextStorage.getStore(), attrs))) {
358
- scope.setExtra(key, value);
359
- }
360
- Sentry.captureException(normalizedError);
361
- });
364
+ let eventId;
365
+ const sentryWithScope = Sentry.withScope;
366
+ const sentryCaptureException = Sentry.captureException;
367
+ if (typeof sentryWithScope === "function" && typeof sentryCaptureException === "function") {
368
+ sentryWithScope((scope) => {
369
+ for (const [key, value] of Object.entries(mergeAttributes(contextStorage.getStore(), attrs))) {
370
+ scope.setExtra(key, value);
371
+ }
372
+ eventId = sentryCaptureException(normalizedError);
373
+ });
374
+ return eventId;
375
+ }
376
+ if (typeof sentryCaptureException === "function") {
377
+ eventId = sentryCaptureException(normalizedError);
378
+ }
379
+ return eventId;
362
380
  }
363
381
  };
364
382
  function withLogContext(context, callback) {
@@ -437,7 +455,7 @@ function logError(eventName, context = {}, attributes = {}, body) {
437
455
  }
438
456
  function logException(error, eventName, context = {}, attributes = {}, body) {
439
457
  const normalizedError = error instanceof Error ? error : new Error(String(error));
440
- log.exception(eventName, normalizedError, toContextAndAttributes(context, attributes), body);
458
+ return log.exception(eventName, normalizedError, toContextAndAttributes(context, attributes), body);
441
459
  }
442
460
  function setTags(context = {}) {
443
461
  setLogContext(context);
@@ -504,6 +522,34 @@ function setSpanStatus(status) {
504
522
  function toOptionalString(value) {
505
523
  return typeof value === "string" && value.trim() ? value : void 0;
506
524
  }
525
+ function getActiveTraceId() {
526
+ const sentry = Sentry2;
527
+ if (typeof sentry.getActiveSpan !== "function" || typeof sentry.spanToJSON !== "function") {
528
+ return void 0;
529
+ }
530
+ try {
531
+ const span = sentry.getActiveSpan();
532
+ if (!span) {
533
+ return void 0;
534
+ }
535
+ return toOptionalString(sentry.spanToJSON(span).trace_id);
536
+ } catch {
537
+ return void 0;
538
+ }
539
+ }
540
+ function resolveErrorReference(eventId) {
541
+ const traceId = getActiveTraceId();
542
+ if (!eventId && !traceId) {
543
+ return null;
544
+ }
545
+ if (!traceId) {
546
+ return null;
547
+ }
548
+ return {
549
+ traceId,
550
+ ...eventId ? { eventId } : {}
551
+ };
552
+ }
507
553
 
508
554
  export {
509
555
  logInfo,
@@ -516,5 +562,6 @@ export {
516
562
  withSpan,
517
563
  setSpanAttributes,
518
564
  setSpanStatus,
519
- toOptionalString
565
+ toOptionalString,
566
+ resolveErrorReference
520
567
  };