Skip to content

Commit 1b8d6aa

Browse files
authored
Merge pull request #1856 from exceptionless/feature/next-project-management
Next: Project Management
2 parents c99debf + b8c32b9 commit 1b8d6aa

File tree

184 files changed

+7349
-1735
lines changed

Some content is hidden

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

184 files changed

+7349
-1735
lines changed

.github/copilot-instructions.md

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Located in the `src/Exceptionless.Web/ClientApp` directory.
3838
- Code can be formatted and linted with `npm run format` and checked for errors with `npm run check` tasks.
3939
- Don't use namespace imports unless importing svelte-shadcn component or from a barrel export index file.
4040
- Limit use of $effect as there is usually a better way to solve the problem like using $derived.
41+
- All single-line control statements in JavaScript must be enclosed in curly braces to ensure unambiguous control flow, enhance readability, and prevent potential errors arising from unintended statement grouping.
4142

4243
- **Architecture & Components:**
4344
- Follow the Composite Component Pattern.

.vscode/launch.json

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4+
{
5+
"name": "Aspire",
6+
"type": "coreclr",
7+
"request": "launch",
8+
"preLaunchTask": "build",
9+
"program": "${workspaceFolder}/src/Exceptionless.AppHost/bin/Debug/net9.0/Exceptionless.AppHost.dll",
10+
"args": [],
11+
"cwd": "${workspaceFolder}/src/Exceptionless.AppHost",
12+
"stopAtEntry": false,
13+
"serverReadyAction": {
14+
"action": "openExternally",
15+
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
16+
},
17+
"env": {
18+
"AppMode": "Development"
19+
}
20+
},
421
{
522
"name": "Web",
623
"type": "coreclr",
724
"request": "launch",
825
"preLaunchTask": "build",
9-
// If you have changed target frameworks, make sure to update the program path.
1026
"program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net9.0/Exceptionless.Web.dll",
1127
"args": [],
1228
"cwd": "${workspaceFolder}/src/Exceptionless.Web",
@@ -24,7 +40,6 @@
2440
"type": "coreclr",
2541
"request": "launch",
2642
"preLaunchTask": "build",
27-
// If you have changed target frameworks, make sure to update the program path.
2843
"program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net9.0/Exceptionless.Job.dll",
2944
"args": [],
3045
"cwd": "${workspaceFolder}",

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"clsx",
1818
"cmdk",
1919
"colour",
20+
"Contoso",
2021
"dockerignore",
2122
"editorconfig",
2223
"elasticsearch",
@@ -43,6 +44,8 @@
4344
"Serilog",
4445
"sessionend",
4546
"shadcn",
47+
"shiki",
48+
"shikijs",
4649
"sonner",
4750
"standardjs",
4851
"superforms",
@@ -51,6 +54,7 @@
5154
"typeschema",
5255
"WCAG",
5356
"websockets",
57+
"Writeline",
5458
"Xunit"
5559
],
5660
"css.customData": [
-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
engine-strict=true
2-
resolution-mode=highest

src/Exceptionless.Web/ClientApp/package-lock.json

+1,530-882
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Exceptionless.Web/ClientApp/package.json

+42-40
Original file line numberDiff line numberDiff line change
@@ -25,70 +25,72 @@
2525
"upgrade": "ncu -i"
2626
},
2727
"devDependencies": {
28-
"@chromatic-com/storybook": "^3.2.5",
29-
"@eslint/compat": "^1.2.7",
30-
"@eslint/js": "^9.22.0",
31-
"@iconify-json/lucide": "^1.2.29",
32-
"@playwright/test": "^1.51.0",
33-
"@storybook/addon-a11y": "^8.6.4",
34-
"@storybook/addon-essentials": "^8.6.4",
35-
"@storybook/addon-interactions": "^8.6.4",
36-
"@storybook/addon-svelte-csf": "^5.0.0-next.26",
37-
"@storybook/blocks": "^8.6.4",
38-
"@storybook/svelte": "^8.6.4",
39-
"@storybook/sveltekit": "^8.6.4",
40-
"@storybook/test": "^8.6.4",
28+
"@chromatic-com/storybook": "^3.2.6",
29+
"@eslint/compat": "^1.2.8",
30+
"@eslint/js": "^9.25.1",
31+
"@iconify-json/lucide": "^1.2.39",
32+
"@playwright/test": "^1.52.0",
33+
"@storybook/addon-a11y": "^8.6.12",
34+
"@storybook/addon-essentials": "^8.6.12",
35+
"@storybook/addon-interactions": "^8.6.12",
36+
"@storybook/addon-svelte-csf": "^5.0.0-next.28",
37+
"@storybook/blocks": "^8.6.12",
38+
"@storybook/svelte": "^8.6.12",
39+
"@storybook/sveltekit": "^8.6.12",
40+
"@storybook/test": "^8.6.12",
4141
"@sveltejs/adapter-static": "^3.0.8",
42-
"@sveltejs/kit": "^2.19.0",
42+
"@sveltejs/kit": "^2.20.7",
4343
"@sveltejs/vite-plugin-svelte": "^5.0.3",
44-
"@tailwindcss/vite": "^4.0.12",
44+
"@tailwindcss/vite": "^4.1.4",
4545
"@testing-library/jest-dom": "^6.6.3",
4646
"@testing-library/svelte": "^5.2.7",
4747
"@types/eslint": "^9.6.1",
48-
"@types/node": "^22.13.10",
48+
"@types/node": "^22.15.2",
4949
"@types/throttle-debounce": "^5.0.2",
5050
"cross-env": "^7.0.3",
51-
"eslint": "^9.22.0",
52-
"eslint-config-prettier": "^10.1.1",
53-
"eslint-plugin-perfectionist": "^4.10.1",
54-
"eslint-plugin-svelte": "^3.1.0",
55-
"jsdom": "^26.0.0",
51+
"eslint": "^9.25.1",
52+
"eslint-config-prettier": "^10.1.2",
53+
"eslint-plugin-perfectionist": "^4.12.3",
54+
"eslint-plugin-svelte": "^3.5.1",
55+
"jsdom": "^26.1.0",
5656
"prettier": "^3.5.3",
5757
"prettier-plugin-svelte": "^3.3.3",
5858
"prettier-plugin-tailwindcss": "^0.6.11",
59-
"storybook": "^8.6.4",
60-
"svelte": "^5.22.6",
61-
"svelte-check": "^4.1.5",
62-
"swagger-typescript-api": "^13.0.23",
59+
"storybook": "^8.6.12",
60+
"svelte": "^5.28.2",
61+
"svelte-check": "^4.1.6",
62+
"swagger-typescript-api": "^13.1.1",
6363
"tslib": "^2.8.1",
64-
"typescript": "^5.8.2",
65-
"typescript-eslint": "^8.26.1",
66-
"vite": "^6.2.1",
67-
"vitest": "3.0.8"
64+
"typescript": "^5.8.3",
65+
"typescript-eslint": "^8.31.0",
66+
"vite": "^6.3.3",
67+
"vitest": "3.1.2"
6868
},
6969
"dependencies": {
7070
"@exceptionless/browser": "^3.1.0",
7171
"@exceptionless/fetchclient": "^0.40.0",
72-
"@tanstack/svelte-query": "https://pkg.pr.new/@tanstack/svelte-query@28f98f9",
73-
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@28f98f9",
72+
"@lucide/svelte": "^0.503.0",
73+
"@tanstack/svelte-query": "https://pkg.pr.new/@tanstack/svelte-query@8c9ce9",
74+
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@8c9ce9",
7475
"@tanstack/svelte-table": "^9.0.0-alpha.10",
7576
"@typeschema/class-validator": "^0.3.0",
76-
"bits-ui": "^1.3.9",
77+
"bits-ui": "^1.3.19",
7778
"class-validator": "^0.14.1",
7879
"clsx": "^2.1.1",
79-
"formsnap": "^2.0.0",
80+
"dompurify": "^3.2.5",
81+
"formsnap": "^2.0.1",
8082
"kit-query-params": "^0.0.26",
81-
"lucide-svelte": "^0.479.0",
82-
"mode-watcher": "^0.5.1",
83-
"oidc-client-ts": "^3.1.0",
83+
"mode-watcher": "^1.0.2",
84+
"oidc-client-ts": "^3.2.0",
8485
"pretty-ms": "^9.2.0",
85-
"runed": "^0.24.0",
86+
"runed": "^0.25.0",
87+
"shiki": "^3.3.0",
8688
"svelte-sonner": "^0.3.28",
8789
"svelte-time": "^2.0.1",
88-
"sveltekit-superforms": "^2.24.0",
89-
"tailwind-merge": "^3.0.2",
90+
"sveltekit-superforms": "^2.24.1",
91+
"tailwind-merge": "^3.2.0",
9092
"tailwind-variants": "^1.0.0",
91-
"tailwindcss": "^4.0.12",
93+
"tailwindcss": "^4.1.4",
9294
"throttle-debounce": "^5.0.2"
9395
},
9496
"type": "module"

src/Exceptionless.Web/ClientApp/resources.md

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### TODO
66

77
- Investigate loading data in - export function load({ url, fetch }) {}
8+
- <https://svelte.dev/docs/svelte/class#The-class:-directive> migrate from cn / cslx
9+
- IsBoolean on model gen.
810

911
#### shadcn svelte upgrade
1012

src/Exceptionless.Web/ClientApp/src/lib/assets/google.svg

-1
This file was deleted.

src/Exceptionless.Web/ClientApp/src/lib/assets/microsoft.svg

-1
This file was deleted.

src/Exceptionless.Web/ClientApp/src/lib/features/auth/index.svelte.ts

+69-26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ import { PersistedState } from 'runed';
66

77
import type { Login, TokenResult } from './models';
88

9+
export interface OAuthLoginOptions extends OAuthPopupOptions {
10+
redirectUrl?: string;
11+
}
12+
13+
export interface OAuthPopupOptions {
14+
authUrl: string;
15+
clientId: string;
16+
extraParams?: Record<string, string>;
17+
popupOptions?: { height: number; width: number };
18+
provider: SupportedOAuthProviders;
19+
scope: string;
20+
}
21+
22+
export interface OAuthResponseData {
23+
code: string;
24+
state: string;
25+
}
26+
27+
export type SupportedOAuthProviders = 'facebook' | 'github' | 'google' | 'live' | 'slack';
28+
929
const authSerializer = {
1030
deserialize: (value: null | string): null | string => {
1131
if (value === '') {
@@ -30,6 +50,7 @@ export const facebookClientId = env.PUBLIC_FACEBOOK_APPID;
3050
export const gitHubClientId = env.PUBLIC_GITHUB_APPID;
3151
export const googleClientId = env.PUBLIC_GOOGLE_APPID;
3252
export const microsoftClientId = env.PUBLIC_MICROSOFT_APPID;
53+
export const slackClientId = env.PUBLIC_SLACK_APPID;
3354
export const enableOAuthLogin = facebookClientId || gitHubClientId || googleClientId || microsoftClientId;
3455

3556
export async function facebookLogin(redirectUrl?: string) {
@@ -128,15 +149,43 @@ export async function logout() {
128149
accessToken.current = null;
129150
}
130151

131-
async function oauthLogin(options: {
132-
authUrl: string;
133-
clientId: string;
134-
extraParams?: Record<string, string>;
135-
popupOptions?: { height: number; width: number };
136-
provider: string;
137-
redirectUrl?: string;
138-
scope: string;
139-
}) {
152+
export async function slackOAuthLogin(): Promise<string> {
153+
if (!slackClientId) {
154+
throw new Error('Slack client id not set');
155+
}
156+
157+
const data = await openOAuthPopup({
158+
authUrl: 'https://slack.com/oauth/authorize',
159+
clientId: slackClientId,
160+
extraParams: {
161+
state: encodeURIComponent(Math.random().toString(36).substring(2))
162+
},
163+
popupOptions: { height: 630, width: 580 },
164+
provider: 'slack',
165+
scope: 'incoming-webhook'
166+
});
167+
168+
return data.code;
169+
}
170+
171+
async function oauthLogin(options: OAuthLoginOptions) {
172+
const data = await openOAuthPopup(options);
173+
174+
const client = useFetchClient();
175+
const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, {
176+
clientId: options.clientId,
177+
code: data.code,
178+
redirectUri: window.location.origin,
179+
state: data.state
180+
});
181+
182+
if (response.ok && response.data?.token) {
183+
accessToken.current = response.data.token;
184+
await goto(options.redirectUrl || '/');
185+
}
186+
}
187+
188+
async function openOAuthPopup(options: OAuthPopupOptions): Promise<OAuthResponseData> {
140189
const width = options.popupOptions?.width || 500;
141190
const height = options.popupOptions?.height || 500;
142191
const features = {
@@ -158,25 +207,19 @@ async function oauthLogin(options: {
158207
);
159208

160209
const url = `${options.authUrl}?${new URLSearchParams(params).toString()}`;
161-
162210
const popup = window.open(url, options.provider, stringifyOptions(features));
163-
popup?.focus();
164-
165-
const data = await waitForUrl(popup!, redirectUrl);
166-
if (options.extraParams?.state && data.state !== options.extraParams.state) throw new Error('Invalid state');
211+
if (!popup) {
212+
throw new Error('Failed to open popup window');
213+
}
167214

168-
const client = useFetchClient();
169-
const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, {
170-
clientId: options.clientId,
171-
code: data.code,
172-
redirectUri: redirectUrl,
173-
state: data.state
174-
});
215+
popup.focus();
175216

176-
if (response.ok && response.data?.token) {
177-
accessToken.current = response.data.token;
178-
await goto(options.redirectUrl || '/');
217+
const data = await waitForUrl(popup!, redirectUrl);
218+
if (options.extraParams?.state && data.state !== options.extraParams.state) {
219+
throw new Error('Invalid state');
179220
}
221+
222+
return data;
180223
}
181224

182225
function stringifyOptions(options: object): string {
@@ -189,7 +232,7 @@ function stringifyOptions(options: object): string {
189232
return parts.join(',');
190233
}
191234

192-
function waitForUrl(popup: Window, redirectUri: string): Promise<{ code: string; state: string }> {
235+
function waitForUrl(popup: Window, redirectUri: string): Promise<OAuthResponseData> {
193236
return new Promise((resolve, reject) => {
194237
const polling = setInterval(() => {
195238
if (!popup || popup.closed || popup.closed === undefined) {
@@ -210,7 +253,7 @@ function waitForUrl(popup: Window, redirectUri: string): Promise<{ code: string;
210253
if ('error' in params && (params as { error: string }).error) {
211254
reject(new Error((params as { error: string }).error));
212255
} else {
213-
resolve(params);
256+
resolve(params as OAuthResponseData);
214257
}
215258
} else {
216259
reject(

src/Exceptionless.Web/ClientApp/src/lib/features/events/api.svelte.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ export function getStackCountQuery(request: GetStackCountRequest) {
230230

231231
return response.data!;
232232
},
233-
queryKey: queryKeys.stacksCount(request.route.stackId)
233+
queryKey: [...queryKeys.stacksCount(request.route.stackId), { params: request.params }]
234234
}));
235235
}
236236

0 commit comments

Comments
 (0)