Skip to content

Commit 85ac670

Browse files
committed
wip: switch to monorepo
1 parent 15ee70b commit 85ac670

60 files changed

Lines changed: 1032 additions & 500 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ jobs:
99
- uses: actions/checkout@v5
1010

1111
- uses: pnpm/action-setup@v4
12+
with:
13+
version: latest
1214

1315
- uses: actions/setup-node@v5.0.0
1416
with:

.gitignore

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
.DS_Store
2-
.vscode/**
3-
node_modules
4-
dist/**
52
coverage/**
6-
yarn-error.log
3+
node_modules
4+
packages/**/dist/**

.oxlintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"ignorePatterns": ["dist"]
3+
}

README.md

Lines changed: 8 additions & 317 deletions
Original file line numberDiff line numberDiff line change
@@ -1,320 +1,11 @@
1-
# Bugsnag zero
1+
# Bugsnag zero monorepo
22

3-
## What is this?
3+
This repository contains the source for the published packages:
44

5-
This is a rebuilt version of the
6-
[bugsnag-js](https://github.com/bugsnag/bugsnag-js) client with the following
7-
goals:
5+
- [`@birchill/bugsnag-zero`](./packages/bugsnag-zero) – the lightweight Bugsnag
6+
client.
7+
- [`@birchill/bugsnag-zero-lambda-context`](./packages/bugsnag-zero-lambda-context) -
8+
a plugin for enriching events with AWS Lambda request metadata.
89

9-
- Reduced bundle size
10-
- Support for non-main thread contexts (e.g. Web workers)
11-
12-
It does this using the following approach:
13-
14-
- Dropping support for older browsers including IE
15-
- Leaning heavily on the plugin approach — every feature is a plugin. As a
16-
result unused features are tree-shaked and don't affect your bundle size
17-
- Being written entirely in TypeScript - by doing more checking at build time we
18-
can drop some runtime checks
19-
20-
It doesn't include support for quite a number of features simply because we
21-
haven't found a need for them yet. Some noteable ones include:
22-
23-
- Sessions
24-
- Inline scripts (these days CSP generally makes inline scripts harder to use
25-
anyway)
26-
- Loggers
27-
- RegExps for redacted keys
28-
- Callbacks for breadcrumbs
29-
- User IP collection (we prefer to respect user privacy)
30-
31-
Many of these could be added, if needed, by adding further plugins.
32-
33-
On the other hand, it adds a few other features:
34-
35-
- The ability to substitute in custom delivery providers (e.g. so you can send
36-
to an SNS topic).
37-
- `Bugsnag.notify()` returns a Promise so you can wait on it to ensure delivery
38-
was successful.
39-
- `Bugsnag.notify()` can take `metadata` and `severity` settings as a object
40-
rather than you having to provide an on-error callback (see below).
41-
- If an `Error` object has a `metadata` field, it will be merged into the
42-
reported error's metadata.
43-
- A `browserHandledRejectionBreadcrumbs` plugin for logging _handled_ rejections.
44-
- Post-error callbacks - called after fully preparing the error but just before
45-
sending it. This was mostly added as a means of supporting "error" breadcrumbs.
46-
- `redactKeys` exports its functions so you can re-use them for other logging
47-
etc.
48-
49-
So far very little effort has been spent on optimizing the code size of the
50-
generated code. A little code golf and manual minification could likely reduce
51-
the bundle size much further still.
52-
53-
## Usage
54-
55-
The easiest way to use this for an existing installation is to use the legacy
56-
config helper, `fromLegacyConfig`. However, note that doing this will produce
57-
much less significant code savings since much less code can be tree-shaken.
58-
59-
Better still is to manually configure the plugins one-by-one according to your
60-
needs. For example,
61-
62-
```typescript
63-
import Bugsnag, {
64-
appDuration,
65-
browserContext,
66-
browserHandledRejectionBreadcrumbs,
67-
browserNotifyUnhandledExceptions,
68-
browserNotifyUnhandledRejections,
69-
consoleBreadcrumbs,
70-
deviceOrientation,
71-
errorBreadcrumbs,
72-
fetchBreadcrumbs,
73-
interactionBreadcrumbs,
74-
limitEvents,
75-
navigationBreadcrumbs,
76-
ReactPlugin,
77-
redactKeys,
78-
stringifyValues,
79-
} from '@birchill/bugsnag-zero';
80-
81-
const plugins = [
82-
appDuration,
83-
browserContext,
84-
browserHandledRejectionBreadcrumbs,
85-
browserNotifyUnhandledExceptions,
86-
browserNotifyUnhandledRejections,
87-
deviceOrientation,
88-
errorBreadcrumbs,
89-
fetchBreadcrumbs,
90-
interactionBreadcrumbs,
91-
limitEvents(10),
92-
navigationBreadcrumbs,
93-
ReactPlugin,
94-
redactKeys(['accessToken', 'password']),
95-
stringifyValues,
96-
];
97-
98-
if (__RELEASE_STAGE__ !== 'test') {
99-
plugins.push(consoleBreadcrumbs);
100-
}
101-
102-
Bugsnag.start({
103-
apiKey: '<apiKey>',
104-
appType: 'browser',
105-
collectUserIp: false,
106-
enabledReleaseStages: ['prod', 'beta'],
107-
plugins,
108-
releaseStage: __RELEASE_STAGE__,
109-
});
110-
```
111-
112-
There are a few API differences from the official client. Hopefully the
113-
TypeScript interfaces make these more obvious.
114-
115-
For example, we've often found it useful to set the severity of an error while
116-
notifying. The official client requires supplying a callback to do this but this
117-
module allows specifying it as a property on the second argument to the notify
118-
callback:
119-
120-
```typescript
121-
// bugsnag-js
122-
Bugsnag.notify(message, (event) => {
123-
event.severity = 'warning';
124-
});
125-
126-
// @birchill/bugsnag-zero
127-
Bugsnag.notify(message, { severity: 'warning' });
128-
```
129-
130-
Similarly, it is possible to specify `metadata` directly on this second
131-
argument.
132-
133-
It is, of course, still possible to pass an error callback as per the official
134-
client's API.
135-
136-
### React plugin
137-
138-
In order to allow using Preact with the React plugin and to avoid introducing a
139-
dependency on React itself just for its types, the typings for the React plugin
140-
are a bit convoluted.
141-
142-
For Preact something like the following is needed:
143-
144-
```typescript
145-
const MyBugsnagErrorBoundary = React.useMemo(
146-
() =>
147-
Bugsnag.getPlugin('react')!.createErrorBoundary<
148-
typeof React.Component,
149-
ComponentType,
150-
VNode
151-
>(React.Component, React.createElement),
152-
[]
153-
);
154-
```
155-
156-
Furthermore, unlike the official bugsnag-js client, we don't allow passing in
157-
React to the constructor. Instead we always require a call to
158-
`createErrorBoundary`.
159-
160-
### Usage with Node.js
161-
162-
We don't properly support Node.js at this time. In particular, there's no
163-
delivery mechanism defined for it. It would be trivial to write, but we haven't
164-
needed it yet.
165-
166-
That said, there are some plugins that should work with node including the
167-
`lambdaContext` plugin for logging from AWS Lambda.
168-
169-
At Birchill, we use a custom `Delivery` class to post the events to an SNS topic
170-
and have that send to Bugsnag since that's faster than having the Lambda wait on
171-
the Bugsnag server and more flexible too (e.g. you can post your events to Slack
172-
etc. too as needed).
173-
174-
The setup looks something like:
175-
176-
```typescript
177-
import Bugsnag, {
178-
appDuration,
179-
errorBreadcrumbs,
180-
lambdaContext,
181-
nodeNotifyUnhandledExceptions,
182-
nodeNotifyUnhandledRejections,
183-
redactKeys,
184-
} from '@birchill/bugsnag-zero';
185-
import { lambdaContext } from '@birchill/bugsnag-zero/lambda-context';
186-
187-
Bugsnag.start({
188-
apiKey: '<unused>',
189-
appType: 'nodejs',
190-
plugins: [
191-
appDuration,
192-
errorBreadcrumbs,
193-
lambdaContext(),
194-
nodeNotifyUnhandledExceptions,
195-
nodeNotifyUnhandledRejections,
196-
redactKeys(keysToRedact),
197-
],
198-
});
199-
200-
Bugsnag.setDelivery({
201-
sendEvent: async ({ events }): Promise<void> => {
202-
const errorClass = events[0].exceptions[0]?.errorClass || 'Unknown';
203-
const context = events[0].context;
204-
const subject = `Error: ${errorClass} in ${context}`;
205-
206-
const publishCommand = new PublishCommand({
207-
Subject: subject,
208-
TopicArn: errorTopicArn,
209-
Message: JSON.stringify(events),
210-
});
211-
212-
await snsClient.send(publishCommand);
213-
},
214-
});
215-
216-
// When a Lambda handler is called, update the lambdaContext plugin:
217-
218-
async function handler(event: Event, context: Context): Promise<void> {
219-
Bugsnag.getPlugin('lambdaContext')?.setContext(event, context);
220-
}
221-
```
222-
223-
### Using a custom user agent string parser
224-
225-
Bugsnag's [v5 reporting API](https://bugsnagerrorreportingapi.docs.apiary.io/#reference/0/notify/send-error-reports)
226-
requires passing in the browser name, browser version, OS name etc. explicitly.
227-
In other words, it requires you to parse the user agent string on the _client_.
228-
229-
By comparison, the v4 API that the official client uses just passes the user
230-
agent string to the API and lets the server parse it.
231-
232-
Adding a full-blown user agent string parser would bloat this library a lot so
233-
we provide a very simple one that covers the basic cases. For example, it
234-
doesn't handle things like bots etc. since hopefully they're probably not going
235-
to be triggering your error reporting (and if they are, the raw user agent
236-
string is still included so you can detect that).
237-
238-
However, perhaps your app already has a user agent string parser included and
239-
you want to re-use that? You can do that by using the
240-
`browserContextWithUaParser` plugin in place of the `browserContext` plugin and
241-
supplying a function that takes a string and returns an object of the following
242-
shape:
243-
244-
```typescript
245-
type UserAgentInfo = {
246-
browserName?: string;
247-
browserVersion?: string;
248-
osName?: string;
249-
osVersion?: string;
250-
manufacturer?: string;
251-
model?: string;
252-
modelNumber?: string;
253-
};
254-
```
255-
256-
For an unrecognized user agent string, you would just return an empty object
257-
(`{}`).
258-
259-
For example:
260-
261-
```typescript
262-
import Bugsnag, { browserContextWithUaParser } from '@birchill/bugsnag-zero';
263-
264-
const myUaParser = new Parser();
265-
const parseUaString = (uaString: string) => {
266-
const result = myUaParser.parse(uaString);
267-
return result
268-
? {
269-
browserName: result.browser,
270-
browserVersion: `${result.major}.${result.minor}`,
271-
}
272-
: {};
273-
};
274-
275-
Bugsnag.start({
276-
apiKey: '<unused>',
277-
appType: 'nodejs',
278-
plugins: [browserContextWithUaParser(parseUaString)],
279-
});
280-
```
281-
282-
This also gives you full control on how browsers are grouped together (e.g. do
283-
you want Chrome on iOS to be treated the same as real Chrome? Do you want an
284-
EdgeHTML version of Edge to be grouped together with Chromium Edge?)
285-
286-
Similarly, if you have no need for user agent string parsing and want to use
287-
`browserContext` without the user agent string parsing bloating your code, the
288-
following should hopefully mean it gets tree-shaken out:
289-
290-
```typescript
291-
import Bugsnag, { browserContextWithUaParser } from '@birchill/bugsnag-zero';
292-
293-
Bugsnag.start({
294-
apiKey: '<unused>',
295-
appType: 'nodejs',
296-
plugins: [browserContextWithUaParser(() => {})],
297-
});
298-
```
299-
300-
Note that none of this is tested at all so let me know if it doesn't work.
301-
302-
## Development
303-
304-
### Building
305-
306-
```
307-
pnpm build
308-
```
309-
310-
### Releasing
311-
312-
```
313-
pnpm release
314-
git push --follow-tags
315-
```
316-
317-
Hopefully GitHub Actions will take care of publishing the release.
318-
319-
(Note that it's going to default to applying the `latest` tag so if we ever need
320-
to publish an update to an older version we'll need to do it manually.)
10+
Each package can be built with `pnpm -r build` and tested individually. See the
11+
respective package directories for full README files and documentation.

lambda-context.d.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

lambda-context.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)