better-ship 0.33.0 → 0.35.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.
Files changed (31) hide show
  1. package/dist/browser-support/floor.d.ts +43 -0
  2. package/dist/browser-support/floor.d.ts.map +1 -0
  3. package/dist/browser-support/floor.js +3 -0
  4. package/dist/browser-support/oxlint.d.ts +6 -0
  5. package/dist/browser-support/oxlint.d.ts.map +1 -0
  6. package/dist/browser-support/oxlint.js +175 -0
  7. package/dist/browser-support/oxlint.js.map +1 -0
  8. package/dist/browser-support/polyfills.d.ts +10 -0
  9. package/dist/browser-support/polyfills.d.ts.map +1 -0
  10. package/dist/browser-support/polyfills.js +3 -0
  11. package/dist/browser-support/update-screen.d.ts +51 -0
  12. package/dist/browser-support/update-screen.d.ts.map +1 -0
  13. package/dist/browser-support/update-screen.js +107 -0
  14. package/dist/browser-support/update-screen.js.map +1 -0
  15. package/dist/floor-Cuw8EIhg.js +146 -0
  16. package/dist/floor-Cuw8EIhg.js.map +1 -0
  17. package/dist/polyfills-DEQFkqRI.js +28 -0
  18. package/dist/polyfills-DEQFkqRI.js.map +1 -0
  19. package/dist/sentry/client.d.ts +21 -0
  20. package/dist/sentry/client.d.ts.map +1 -1
  21. package/dist/sentry/client.js +23 -1
  22. package/dist/sentry/client.js.map +1 -1
  23. package/package.json +21 -1
  24. package/src/infrastructure/browser-support/.browserslistrc +5 -0
  25. package/src/infrastructure/browser-support/browser-floor.error.ts +31 -0
  26. package/src/infrastructure/browser-support/floor.ts +145 -0
  27. package/src/infrastructure/browser-support/oxlint/index.ts +13 -0
  28. package/src/infrastructure/browser-support/oxlint/no-unsupported-api.ts +219 -0
  29. package/src/infrastructure/browser-support/polyfills.ts +25 -0
  30. package/src/infrastructure/browser-support/update-screen.ts +142 -0
  31. package/src/infrastructure/sentry/client.ts +41 -0
@@ -0,0 +1,142 @@
1
+ /** Set on `documentElement` when the update screen is showing. Hydration checks this name. */
2
+ export const unsupportedBrowserAttribute = 'data-unsupported-browser'
3
+
4
+ const updateTitleAttribute = 'data-update-title' as const
5
+ const updateBodyAttribute = 'data-update-body' as const
6
+ const fallbackFont = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif'
7
+
8
+ /** Copy and public URLs for the update screen tags in the document head. */
9
+ export type UpdateScreenHead = {
10
+ readonly title: string
11
+ readonly body: string
12
+ /** Public URL of `updateScreenStyle`. */
13
+ readonly styleHref: string
14
+ /** Public URL of `updateScreenScript`. */
15
+ readonly scriptSrc: string
16
+ }
17
+
18
+ /** Stylesheet link a root route places in `head().links`. */
19
+ export type UpdateScreenLink = {
20
+ readonly rel: 'stylesheet'
21
+ readonly href: string
22
+ }
23
+
24
+ /** Deferred script a root route places in `head().scripts`. */
25
+ export type UpdateScreenScript = {
26
+ readonly src: string
27
+ readonly defer: true
28
+ readonly 'data-update-title': string
29
+ readonly 'data-update-body': string
30
+ }
31
+
32
+ /**
33
+ * `links` and `scripts` a root route spreads into `head()`.
34
+ * The arrays are mutable so they match the `head()` return type.
35
+ */
36
+ export type UpdateScreenHeadEntries = {
37
+ links: UpdateScreenLink[]
38
+ scripts: UpdateScreenScript[]
39
+ }
40
+
41
+ /**
42
+ * Stylesheet and deferred script entries for a root route `head()`.
43
+ * Spread `links` and `scripts` into `head()`. The router escapes the attribute values.
44
+ * `defer` keeps parsing unblocked. The script is classic ES5, so a browser below the floor can show the screen.
45
+ */
46
+ export function updateScreenHeadScript(head: UpdateScreenHead): UpdateScreenHeadEntries {
47
+ return {
48
+ links: [{ rel: 'stylesheet', href: head.styleHref }],
49
+ scripts: [
50
+ {
51
+ src: head.scriptSrc,
52
+ defer: true,
53
+ [updateTitleAttribute]: head.title,
54
+ [updateBodyAttribute]: head.body,
55
+ },
56
+ ],
57
+ }
58
+ }
59
+
60
+ /** True when the update screen script marked this element. Skip hydration when it is true. */
61
+ export function isUnsupportedBrowser(root: {
62
+ readonly hasAttribute: (name: string) => boolean
63
+ }): boolean {
64
+ return root.hasAttribute(unsupportedBrowserAttribute)
65
+ }
66
+
67
+ /**
68
+ * Classic ES5 script. Serve it at `scriptSrc`.
69
+ * It shows the screen when `CSS.registerProperty` or `color-mix()` is missing, and does nothing at the floor.
70
+ */
71
+ export const updateScreenScript = `(function () {
72
+ var cssApi = typeof CSS === 'undefined' ? null : CSS;
73
+ var hasProperty = cssApi !== null && typeof cssApi.registerProperty === 'function';
74
+ var hasMix = cssApi !== null && typeof cssApi.supports === 'function' && cssApi.supports('color', 'color-mix(in srgb, red, blue)');
75
+ if (hasProperty && hasMix) return;
76
+ document.documentElement.setAttribute('${unsupportedBrowserAttribute}', '');
77
+ var scripts = document.getElementsByTagName('script');
78
+ var title = 'Update your browser';
79
+ var body = '';
80
+ for (var index = 0; index < scripts.length; index = index + 1) {
81
+ var marker = scripts[index].getAttribute('${updateTitleAttribute}');
82
+ if (marker === null) continue;
83
+ title = marker;
84
+ var text = scripts[index].getAttribute('${updateBodyAttribute}');
85
+ body = text === null ? '' : text;
86
+ break;
87
+ }
88
+ var screen = document.createElement('div');
89
+ screen.id = 'unsupported-browser';
90
+ var heading = document.createElement('h1');
91
+ heading.className = 'unsupported-browser-title';
92
+ heading.textContent = title;
93
+ var paragraph = document.createElement('p');
94
+ paragraph.className = 'unsupported-browser-body';
95
+ paragraph.textContent = body;
96
+ screen.appendChild(heading);
97
+ screen.appendChild(paragraph);
98
+ document.body.appendChild(screen);
99
+ })();
100
+ `
101
+
102
+ // `!important` keeps application styles from painting over the update screen.
103
+ /** Classic CSS for the update screen. Serve it at `styleHref`. Tokens fall back to neutral grey and a system sans-serif. */
104
+ export const updateScreenStyle = `html[${unsupportedBrowserAttribute}],
105
+ html[${unsupportedBrowserAttribute}] body {
106
+ margin: 0;
107
+ min-height: 100%;
108
+ background: var(--band, #f5f5f5);
109
+ }
110
+ html[${unsupportedBrowserAttribute}] body > :not(#unsupported-browser) {
111
+ display: none !important;
112
+ }
113
+ #unsupported-browser {
114
+ box-sizing: border-box;
115
+ min-height: 100vh;
116
+ margin: 0;
117
+ padding: 48px 24px;
118
+ display: flex;
119
+ flex-direction: column;
120
+ justify-content: center;
121
+ align-items: center;
122
+ text-align: center;
123
+ background: var(--band, #f5f5f5);
124
+ color: var(--foreground, #171717);
125
+ font-family: var(--font-text, ${fallbackFont});
126
+ }
127
+ .unsupported-browser-title {
128
+ margin: 0 0 12px;
129
+ max-width: 28rem;
130
+ font-family: var(--font-brand, ${fallbackFont});
131
+ font-size: 2rem;
132
+ font-weight: 500;
133
+ line-height: 1.2;
134
+ }
135
+ .unsupported-browser-body {
136
+ margin: 0;
137
+ max-width: 32rem;
138
+ color: var(--muted-foreground, #525252);
139
+ font-size: 1.0625rem;
140
+ line-height: 1.5;
141
+ }
142
+ `
@@ -33,6 +33,47 @@ export function createSentryLoggerErrorHook(): LoggerErrorHook {
33
33
  }
34
34
  }
35
35
 
36
+ /** The error value as Sentry's React handler receives it. */
37
+ type ReactRootError = Parameters<ReturnType<typeof Sentry.reactErrorHandler>>[0]
38
+
39
+ /** The details React passes to a root error option; React may omit the component stack. */
40
+ export type ReactRootErrorInfo = { readonly componentStack?: string | undefined }
41
+
42
+ /** A `hydrateRoot` error option in React's own shape. */
43
+ type ReactRootErrorHandler = (error: ReactRootError, errorInfo: ReactRootErrorInfo) => void
44
+
45
+ /** The `hydrateRoot` error options; each one replaces React's console default. */
46
+ export type SentryReactRootOptions = {
47
+ readonly onCaughtError: ReactRootErrorHandler
48
+ readonly onUncaughtError: ReactRootErrorHandler
49
+ readonly onRecoverableError: ReactRootErrorHandler
50
+ }
51
+
52
+ /**
53
+ * Report React errors to Sentry from `hydrateRoot`, as Sentry's React 19 setup does.
54
+ * Covers errors that a boundary catches, errors that no boundary catches, and hydration
55
+ * errors that React recovers from. TanStack redirects and not-found control flow are not
56
+ * reported. Each error still goes to the console, as with React's defaults.
57
+ */
58
+ export function createSentryReactRootOptions(): SentryReactRootOptions {
59
+ const handler = Sentry.reactErrorHandler((error, errorInfo) => {
60
+ console.error(error, errorInfo.componentStack)
61
+ })
62
+ // Sentry's handler takes `null` for a missing component stack.
63
+ const report: ReactRootErrorHandler = (error, { componentStack }) => {
64
+ handler(error, { componentStack: componentStack ?? null })
65
+ }
66
+ return {
67
+ // TanStack Router renders not-found through a React error boundary, so each 404 arrives here.
68
+ onCaughtError: (error, errorInfo) => {
69
+ if (isNotFound(error) || isRedirect(error)) return
70
+ report(error, errorInfo)
71
+ },
72
+ onUncaughtError: report,
73
+ onRecoverableError: report,
74
+ }
75
+ }
76
+
36
77
  /** Initialize once in the browser entrypoint before hydration; server imports have no side effects. */
37
78
  export function initializeSentry(options: SentryClientOptions): ReturnType<typeof Sentry.init> {
38
79
  const client = Sentry.init(createSentryClientOptions(options))