fg-devkit 1.5.3 → 1.6.1

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 (46) hide show
  1. package/configs/dependency-policy.vue-workspace.json +3 -3
  2. package/configs/vscode-settings.json +1 -1
  3. package/package.json +9 -1
  4. package/scaffold/vue/.browserslistrc +3 -0
  5. package/scaffold/vue/.editorconfig +5 -0
  6. package/scaffold/vue/Dockerfile +18 -0
  7. package/scaffold/vue/Dockerfile.local +16 -0
  8. package/scaffold/vue/Jenkinsfile +130 -0
  9. package/scaffold/vue/LICENSE +19 -0
  10. package/scaffold/vue/env-adjustment.sh +19 -0
  11. package/scaffold/vue/index.html +16 -0
  12. package/scaffold/vue/nginx/default.conf +23 -0
  13. package/scaffold/vue/nginx/gzip.conf +10 -0
  14. package/scaffold/vue/nginx/security.conf +6 -0
  15. package/scaffold/vue/package.json +44 -0
  16. package/scaffold/vue/poeditor/.poeditor.json +8 -0
  17. package/scaffold/vue/public/config.json +8 -0
  18. package/scaffold/vue/src/App.vue +10 -0
  19. package/scaffold/vue/src/api/client.ts +114 -0
  20. package/scaffold/vue/src/api/runtimeConfig.ts +10 -0
  21. package/scaffold/vue/src/assets/.gitkeep +0 -0
  22. package/scaffold/vue/src/components/.gitkeep +0 -0
  23. package/scaffold/vue/src/composables/.gitkeep +0 -0
  24. package/scaffold/vue/src/config/env.ts +45 -0
  25. package/scaffold/vue/src/i18n.ts +11 -0
  26. package/scaffold/vue/src/lang/sv.json +1 -0
  27. package/scaffold/vue/src/main.ts +29 -0
  28. package/scaffold/vue/src/queries/.gitkeep +0 -0
  29. package/scaffold/vue/src/router/index.ts +22 -0
  30. package/scaffold/vue/src/style.css +7 -0
  31. package/scaffold/vue/src/types/.gitkeep +0 -0
  32. package/scaffold/vue/src/views/HomeView.vue +10 -0
  33. package/scaffold/vue/src/vite-env.d.ts +1 -0
  34. package/scaffold/vue/start-container.sh +15 -0
  35. package/scaffold/vue/tailwind.config.js +21 -0
  36. package/scaffold/vue/vite.config.ts +41 -0
  37. package/scripts/fg-devkit.mjs +28 -3
  38. package/scripts/fleet-bump-fg-devkit.mjs +135 -0
  39. package/scripts/poeditor-common.mjs +118 -0
  40. package/scripts/poeditor-download.mjs +101 -0
  41. package/scripts/poeditor-upload.mjs +58 -0
  42. package/scripts/policy-upgrade.mjs +3 -0
  43. package/scripts/refresh-dependency-policy-from-repos.mjs +7 -0
  44. package/scripts/scaffold-vue.mjs +183 -0
  45. package/scripts/sync-scaffold-vue-deps.mjs +68 -0
  46. package/tsconfig.json +13 -0
@@ -23,7 +23,7 @@
23
23
  "content-disposition-header": "0.6.0",
24
24
  "date-fns": "4.1.0",
25
25
  "element-plus": "2.9.7",
26
- "fds-vue-core": "6.2.5",
26
+ "fds-vue-core": "6.2.6",
27
27
  "imask": "7.6.1",
28
28
  "leaflet": "1.8.0",
29
29
  "lexical": "0.30.0",
@@ -56,7 +56,7 @@
56
56
  "@storybook/addon-vitest": "10.2.12",
57
57
  "@storybook/vue3": "10.2.12",
58
58
  "@storybook/vue3-vite": "10.2.12",
59
- "@tanstack/vue-query-devtools": "6.1.18",
59
+ "@tanstack/vue-query-devtools": "6.1.20",
60
60
  "@tsconfig/node22": "22.0.5",
61
61
  "@types/lodash-es": "4.17.12",
62
62
  "@types/node": "22.16.5",
@@ -64,7 +64,7 @@
64
64
  "@vue/compiler-dom": "3.5.33",
65
65
  "@vue/eslint-config-standard": "8.0.1",
66
66
  "@vue/server-renderer": "3.5.33",
67
- "@vue/test-utils": "2.4.6",
67
+ "@vue/test-utils": "2.4.8",
68
68
  "@vuepic/vue-datepicker": "6.0.3",
69
69
  "arg": "5.0.2",
70
70
  "babel-eslint": "10.1.0",
@@ -36,9 +36,9 @@
36
36
  "editor.parameterHints.enabled": false,
37
37
  "editor.inlayHints.enabled": "off",
38
38
  "editor.suggest.filterGraceful": true,
39
+ "eslint.nodePath": "node_modules/.pnpm",
39
40
  "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
40
41
  "css.customData": [".vscode/tailwind.json"],
41
- "typescript.tsdk": "node_modules/typescript/lib",
42
42
  "typescript.enablePromptUseWorkspaceTsdk": true,
43
43
  "typescript.preferences.includePackageJsonAutoImports": "on",
44
44
  "typescript.suggest.autoImports": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fg-devkit",
3
- "version": "1.5.3",
3
+ "version": "1.6.1",
4
4
  "description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -32,11 +32,17 @@
32
32
  "./scripts/policy-vulns.mjs": "./scripts/policy-vulns.mjs",
33
33
  "./scripts/repo-vulns.mjs": "./scripts/repo-vulns.mjs",
34
34
  "./scripts/fleet-vulns.mjs": "./scripts/fleet-vulns.mjs",
35
+ "./scripts/fleet-bump-fg-devkit.mjs": "./scripts/fleet-bump-fg-devkit.mjs",
35
36
  "./scripts/policy-upgrade.mjs": "./scripts/policy-upgrade.mjs",
36
37
  "./scripts/check-dependency-policy.mjs": "./scripts/check-dependency-policy.mjs",
37
38
  "./scripts/sync-dependency-policy.mjs": "./scripts/sync-dependency-policy.mjs",
38
39
  "./scripts/resolve-dependency-policy-path.mjs": "./scripts/resolve-dependency-policy-path.mjs",
39
40
  "./scripts/refresh-dependency-policy-from-repos.mjs": "./scripts/refresh-dependency-policy-from-repos.mjs",
41
+ "./scripts/sync-scaffold-vue-deps.mjs": "./scripts/sync-scaffold-vue-deps.mjs",
42
+ "./scripts/poeditor-common.mjs": "./scripts/poeditor-common.mjs",
43
+ "./scripts/poeditor-download.mjs": "./scripts/poeditor-download.mjs",
44
+ "./scripts/poeditor-upload.mjs": "./scripts/poeditor-upload.mjs",
45
+ "./scripts/scaffold-vue.mjs": "./scripts/scaffold-vue.mjs",
40
46
  "./scripts/fg-devkit.mjs": "./scripts/fg-devkit.mjs",
41
47
  "./scripts/release.sh": "./scripts/release.sh",
42
48
  "./scripts/cz/cz-adapter.cjs": "./scripts/cz/cz-adapter.cjs",
@@ -45,7 +51,9 @@
45
51
  },
46
52
  "files": [
47
53
  "configs",
54
+ "scaffold",
48
55
  "scripts",
56
+ "tsconfig.json",
49
57
  ".cursor/rules/core-coding-standards.mdc"
50
58
  ],
51
59
  "engines": {
@@ -0,0 +1,3 @@
1
+ > 1%
2
+ last 2 versions
3
+ not dead
@@ -0,0 +1,5 @@
1
+ [*.{js,jsx,ts,tsx,vue}]
2
+ indent_style = space
3
+ indent_size = 2
4
+ trim_trailing_whitespace = true
5
+ insert_final_newline = true
@@ -0,0 +1,18 @@
1
+ FROM nginx
2
+ RUN echo "daemon off;" >> /etc/nginx/nginx.conf
3
+ ENV CONFIG_FILE_PATH=/usr/share/nginx/html/config.json
4
+ RUN apt-get update && apt-get install -y vim curl
5
+ COPY nginx/*.conf /etc/nginx/conf.d/
6
+ COPY dist /usr/share/nginx/html
7
+ COPY start-container.sh /usr/local/bin/start-container
8
+ RUN chmod +x /usr/local/bin/start-container
9
+ COPY build_date.txt build_hash.txt version.txt ./
10
+
11
+ COPY env-adjustment.sh /env-adjustment.sh
12
+ RUN cat /env-adjustment.sh > /tempentrypoint.sh
13
+ RUN tail -n+2 /usr/local/bin/start-container >> /tempentrypoint.sh
14
+ RUN cat /tempentrypoint.sh > /usr/local/bin/start-container
15
+ RUN rm -f /tempentrypoint.sh
16
+
17
+ ENTRYPOINT ["start-container"]
18
+ CMD ["nginx"]
@@ -0,0 +1,16 @@
1
+ FROM node:22-bookworm AS build
2
+ WORKDIR /src
3
+ COPY . /src
4
+ RUN corepack enable && corepack prepare pnpm@10.17.1 --activate
5
+ RUN pnpm install
6
+ RUN pnpm build
7
+
8
+ FROM nginx
9
+ RUN echo "daemon off;" >> /etc/nginx/nginx.conf
10
+ ENV CONFIG_FILE_PATH=/usr/share/nginx/html/config.json
11
+ COPY --from=build /src/dist /usr/share/nginx/html
12
+ COPY nginx/*.conf /etc/nginx/conf.d/
13
+ COPY start-container.sh /usr/local/bin/start-container
14
+ RUN chmod +x /usr/local/bin/start-container
15
+ ENTRYPOINT ["start-container"]
16
+ CMD ["nginx"]
@@ -0,0 +1,130 @@
1
+ @Library('PLACEHOLDER_JENKINS_SHARED_LIBRARY')_
2
+
3
+ pipeline {
4
+ environment {
5
+ registry="PLACEHOLDER_DOCKER_REGISTRY_IMAGE"
6
+ registryCredential='PLACEHOLDER_DOCKER_REGISTRY_CREDENTIAL_ID'
7
+ POEDITOR_API_KEY = credentials('PLACEHOLDER_POEDITOR_CREDENTIAL_ID')
8
+ devBranch='PLACEHOLDER_PRIMARY_DEV_BRANCH'
9
+ NPM_CONFIG_STORE_DIR='/home/jenkins/.cache/pnpm-store'
10
+ }
11
+
12
+ agent any
13
+ options {
14
+ ansiColor('xterm')
15
+ }
16
+ stages {
17
+ stage('Run tests') {
18
+ agent {
19
+ docker {
20
+ image "PLACEHOLDER_JENKINS_NODE_IMAGE"
21
+ args "-v /home/jenkins/.cache:/home/jenkins/.cache:rw,z -v /home/jenkins/tools:/home/jenkins/tools:rw,z"
22
+ }
23
+ }
24
+ stages {
25
+ stage('Dependency Check: OWASP Scan') {
26
+ when {
27
+ not {
28
+ expression {
29
+ env.BRANCH_NAME?.contains('dependabot')
30
+ }
31
+ }
32
+ }
33
+ steps {
34
+ owaspDependencyCheck()
35
+ }
36
+ }
37
+ stage('Install dependencies') {
38
+ steps {
39
+ sh '''
40
+ pnpm store path
41
+ pnpm install --frozen-lockfile --prefer-offline
42
+ '''
43
+ }
44
+ }
45
+ stage('Lint ESLint') {
46
+ steps {
47
+ sh '''pnpm run lint-check'''
48
+ }
49
+ }
50
+ stage('Lint TypeScript') {
51
+ steps {
52
+ sh '''pnpm run lint:ts'''
53
+ }
54
+ }
55
+ }
56
+ }
57
+ stage('Build and publish application') {
58
+ when {
59
+ anyOf {
60
+ branch 'master'
61
+ branch devBranch
62
+ buildingTag()
63
+ }
64
+ }
65
+ stages {
66
+ stage('Build dist') {
67
+ agent {
68
+ docker {
69
+ image "PLACEHOLDER_JENKINS_NODE_IMAGE"
70
+ args "-v /home/jenkins/.cache:/home/jenkins/.cache:rw,z"
71
+ }
72
+ }
73
+ steps {
74
+ sh '''
75
+ pnpm store path
76
+ pnpm install --frozen-lockfile --prefer-offline
77
+ '''
78
+ sh 'sed -i "s/POEDITOR_DUMMY_API_KEY/${POEDITOR_API_KEY}/" ./public/config.json'
79
+ sh 'pnpm run poeditor:download'
80
+ sh 'pnpm run build'
81
+ stash includes: 'dist/**/*', name: 'distfiles'
82
+ }
83
+ }
84
+ stage('Build and push Docker image') {
85
+ agent {
86
+ node {
87
+ label 'PLACEHOLDER_JENKINS_DOCKER_BUILD_LABEL'
88
+ }
89
+ }
90
+ steps {
91
+ checkout scm
92
+ unstash 'distfiles'
93
+ script {
94
+ def tag = sh(returnStdout: true, script: "git tag --contains | head -1 | sed 's/^v//'").trim()
95
+ def build_hash = sh(returnStdout: true, script: "git log --pretty=format:'%h' -n 1").trim()
96
+ writeFile file: 'build_hash.txt', text: build_hash
97
+ def build_date = sh(returnStdout: true, script: "git log --pretty=format:'%cI' -n 1").trim()
98
+ writeFile file: 'build_date.txt', text: build_date
99
+ def version = tag
100
+ if (env.BRANCH_NAME == 'master') {
101
+ tag = 'latest'
102
+ version = build_hash + '-' + build_date
103
+
104
+ if (devBranch == 'master') {
105
+ writeFile file: 'version.txt', text: version
106
+ def image = docker.build(registry + ':' + tag)
107
+ image.push()
108
+ }
109
+ }
110
+
111
+ if (env.BRANCH_NAME == devBranch) {
112
+ tag = 'dev'
113
+ version = build_hash + '-' + build_date
114
+ }
115
+ writeFile file: 'version.txt', text: version
116
+ def image = docker.build(registry + ':' + tag)
117
+ image.push()
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ post {
125
+ always {
126
+ slackBuildStatus(currentBuild.currentResult)
127
+ deleteDir()
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,19 @@
1
+ PROPRIETARY LICENSE
2
+
3
+ Copyright (c) PLACEHOLDER_COPYRIGHT_YEAR PLACEHOLDER_COPYRIGHT_HOLDER
4
+
5
+ All rights reserved.
6
+
7
+ This software and associated documentation files (the "Software") are the
8
+ proprietary and confidential property of PLACEHOLDER_COPYRIGHT_HOLDER.
9
+
10
+ The Software is protected by copyright laws and international copyright
11
+ treaties, as well as other intellectual property laws and treaties.
12
+
13
+ Unauthorized copying, modification, distribution, or use of this Software,
14
+ via any medium, is strictly prohibited and may result in severe civil and
15
+ criminal penalties.
16
+
17
+ The Software is provided "AS IS", without warranty of any kind, express or
18
+ implied, including but not limited to the warranties of merchantability,
19
+ fitness for a particular purpose and noninfringement.
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ touch ~/newvars.env
3
+ env | while IFS= read -r line; do
4
+ value=${line#*=}
5
+ name=${line%%=*}
6
+ nameupper=${name^^}
7
+ if [[ "$nameupper" == *_FILE ]]
8
+ then
9
+ if [ -n "$value" ] && [ -f "$value" ]
10
+ then
11
+ file_value=`cat ${value}`
12
+ varname=${nameupper::-5}
13
+ echo "export ${varname}='${file_value}'" >> ~/newvars.env
14
+ echo "export VITE_${varname}='${file_value}'" >> ~/newvars.env
15
+ fi
16
+ fi
17
+ done
18
+ . ~/newvars.env
19
+ rm ~/newvars.env
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="sv">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
+ <title>PLACEHOLDER_APP_NAME</title>
8
+ </head>
9
+ <body class="font-main text-base font-normal tracking-wide text-gray-900 bg-blue-100 leading-lg antialiased">
10
+ <noscript>
11
+ <strong>This application requires JavaScript.</strong>
12
+ </noscript>
13
+ <div id="app"></div>
14
+ <script type="module" src="/src/main.ts"></script>
15
+ </body>
16
+ </html>
@@ -0,0 +1,23 @@
1
+ server {
2
+ listen 80;
3
+ server_name localhost;
4
+ large_client_header_buffers 4 32k;
5
+ index index.html;
6
+ root /usr/share/nginx/html;
7
+
8
+ location / {
9
+ index index.html;
10
+ try_files $uri $uri/ /index.html;
11
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
12
+ add_header Pragma "no-cache" always;
13
+ add_header Expires "0" always;
14
+
15
+ etag off;
16
+ if_modified_since off;
17
+ }
18
+
19
+ error_page 500 502 503 504 /50x.html;
20
+ location = /50x.html {
21
+ root /usr/share/nginx/html;
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ gzip on;
2
+ gzip_disable "msie6";
3
+
4
+ gzip_vary on;
5
+ gzip_proxied any;
6
+ gzip_comp_level 6;
7
+ gzip_buffers 16 8k;
8
+ gzip_http_version 1.1;
9
+ gzip_min_length 256;
10
+ gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon application/font-woff application/font-woff2;
@@ -0,0 +1,6 @@
1
+ add_header Server "-";
2
+ add_header Strict-Transport-Security "max-age=31536000" always;
3
+ add_header X-Content-Type-Options "nosniff" always;
4
+ add_header X-Frame-Options SAMEORIGIN;
5
+ add_header Referrer-Policy "no-referrer-when-downgrade";
6
+ add_header X-XSS-Protection "1; mode=block" always;
@@ -0,0 +1,44 @@
1
+ {
2
+ "type": "module",
3
+ "name": "PLACEHOLDER_APP_SLUG",
4
+ "version": "1.0.0",
5
+ "private": true,
6
+ "scripts": {
7
+ "serve": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "lint:ts": "pnpm exec fgd typecheck",
11
+ "lint": "pnpm exec fgd lint --fix .",
12
+ "lint-check": "pnpm exec fgd lint .",
13
+ "lint:all": "pnpm lint && pnpm lint:ts",
14
+ "poeditor:download": "pnpm exec fg-devkit poeditor-download --format json",
15
+ "poeditor:upload": "pnpm exec fg-devkit poeditor-upload --language sv --file ./src/lang/sv.json"
16
+ },
17
+ "dependencies": {
18
+ "@tailwindcss/postcss": "4.2.4",
19
+ "@tailwindcss/vite": "4.2.4",
20
+ "@tanstack/vue-query": "5.99.2",
21
+ "axios": "1.15.2",
22
+ "content-disposition-header": "0.6.0",
23
+ "date-fns": "4.1.0",
24
+ "dotenv": "16.4.5",
25
+ "fds-vue-core": "6.2.6",
26
+ "pinia": "3.0.4",
27
+ "qrcode.vue": "3.8.1",
28
+ "vue": "3.5.33",
29
+ "vue-i18n": "11.3.2",
30
+ "vue-router": "4.6.3"
31
+ },
32
+ "devDependencies": {
33
+ "@intlify/unplugin-vue-i18n": "11.0.7",
34
+ "@tanstack/vue-query-devtools": "6.1.20",
35
+ "@types/node": "22.16.5",
36
+ "@vitejs/plugin-vue": "6.0.6",
37
+ "fg-devkit": "1.6.0",
38
+ "start-server-and-test": "2.1.3",
39
+ "tailwindcss": "4.2.4",
40
+ "tsx": "4.21.0",
41
+ "vite": "7.3.2"
42
+ },
43
+ "packageManager": "pnpm@10.17.1"
44
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "projectId": "PLACEHOLDER_POEDITOR_PROJECT_ID",
3
+ "downloadPath": "./src/lang/",
4
+ "languages": ["sv", "en"],
5
+ "defaultLanguage": "sv",
6
+ "defaultFormat": "json",
7
+ "uploadFile": "./src/lang/sv.json"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "APP_API_URL": "http://localhost:8080/",
3
+ "POEDITOR_API_KEY": "POEDITOR_DUMMY_API_KEY",
4
+ "VERSION": {
5
+ "hash": "dev",
6
+ "date": "1970-01-01T00:00:00Z"
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ <script setup lang="ts">
2
+ import { VueQueryDevtools } from '@tanstack/vue-query-devtools'
3
+ </script>
4
+
5
+ <template>
6
+ <div>
7
+ <router-view />
8
+ <VueQueryDevtools />
9
+ </div>
10
+ </template>
@@ -0,0 +1,114 @@
1
+ import { QueryClient } from '@tanstack/vue-query'
2
+ import axios, { type AxiosError, type AxiosRequestConfig, type AxiosResponse } from 'axios'
3
+ import { getApiBaseUrl } from './runtimeConfig'
4
+
5
+ export const api = axios.create({
6
+ timeout: Infinity,
7
+ })
8
+
9
+ api.interceptors.request.use((config) => {
10
+ const baseURL = getApiBaseUrl()
11
+
12
+ if (!baseURL) {
13
+ return Promise.reject(new Error('baseURL missing - request blocked'))
14
+ }
15
+
16
+ config.baseURL = baseURL
17
+ config.headers = config.headers || {}
18
+
19
+ return config
20
+ })
21
+
22
+ type EnhancedAxiosError = AxiosError & { data?: unknown }
23
+
24
+ const handleError = (error: unknown): never => {
25
+ const axiosError = error as AxiosError
26
+ if (axiosError.response?.data) {
27
+ ;(axiosError as EnhancedAxiosError).data = axiosError.response.data
28
+ }
29
+ return Promise.reject(axiosError) as never
30
+ }
31
+
32
+ export const useApi = () => {
33
+ const get = async <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
34
+ try {
35
+ const { data } = await api.get<T>(url, config)
36
+ return data
37
+ } catch (error) {
38
+ return handleError(error)
39
+ }
40
+ }
41
+
42
+ const post = async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
43
+ try {
44
+ const { data } = await api.post<T>(url, body, config)
45
+ return data
46
+ } catch (error) {
47
+ return handleError(error)
48
+ }
49
+ }
50
+
51
+ const postWithResponse = async <T>(
52
+ url: string,
53
+ body?: unknown,
54
+ config?: AxiosRequestConfig,
55
+ ): Promise<AxiosResponse<T>> => {
56
+ try {
57
+ return await api.post<T>(url, body, config)
58
+ } catch (error) {
59
+ return handleError(error)
60
+ }
61
+ }
62
+
63
+ const put = async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
64
+ try {
65
+ const { data } = await api.put<T>(url, body, config)
66
+ return data
67
+ } catch (error) {
68
+ return handleError(error)
69
+ }
70
+ }
71
+
72
+ const patch = async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
73
+ try {
74
+ const { data } = await api.patch<T>(url, body, config)
75
+ return data
76
+ } catch (error) {
77
+ return handleError(error)
78
+ }
79
+ }
80
+
81
+ const del = async <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
82
+ try {
83
+ const { data } = await api.delete<T>(url, config)
84
+ return data
85
+ } catch (error) {
86
+ return handleError(error)
87
+ }
88
+ }
89
+
90
+ return { get, post, postWithResponse, put, patch, del }
91
+ }
92
+
93
+ const NO_RETRY_STATUSES = [400, 401, 403, 404, 406, 409, 422, 498, 500]
94
+ const MAX_RETRIES = 3
95
+
96
+ const retryFunction = (failureCount: number, error: unknown): boolean => {
97
+ if (failureCount >= MAX_RETRIES) return false
98
+
99
+ const axiosError = error as AxiosError
100
+ const status = axiosError?.response?.status
101
+
102
+ return status ? !NO_RETRY_STATUSES.includes(status) : true
103
+ }
104
+
105
+ export const queryClient = new QueryClient({
106
+ defaultOptions: {
107
+ queries: {
108
+ retry: retryFunction,
109
+ },
110
+ mutations: {
111
+ retry: retryFunction,
112
+ },
113
+ },
114
+ })
@@ -0,0 +1,10 @@
1
+ let apiBaseUrl = ''
2
+
3
+ export function setApiBaseUrlFromConfig(config: Record<string, unknown>) {
4
+ const value = config.APP_API_URL
5
+ apiBaseUrl = typeof value === 'string' ? value : ''
6
+ }
7
+
8
+ export function getApiBaseUrl() {
9
+ return apiBaseUrl
10
+ }
File without changes
File without changes
File without changes
@@ -0,0 +1,45 @@
1
+ type EnvironmentConstants = {
2
+ environment: 'localhost' | 'development' | 'preprod' | 'production'
3
+ }
4
+
5
+ const local: EnvironmentConstants = {
6
+ environment: 'localhost',
7
+ }
8
+
9
+ const development: EnvironmentConstants = {
10
+ environment: 'development',
11
+ }
12
+
13
+ const preprod: EnvironmentConstants = {
14
+ environment: 'preprod',
15
+ }
16
+
17
+ const production: EnvironmentConstants = {
18
+ environment: 'production',
19
+ }
20
+
21
+ /**
22
+ * Map hostnames to deployment environments. Replace placeholders with your own hosts.
23
+ */
24
+ const getEnvironment = (): EnvironmentConstants => {
25
+ const { hostname, host } = window.location
26
+
27
+ if (hostname === 'localhost') {
28
+ return local
29
+ }
30
+
31
+ switch (host) {
32
+ case 'PLACEHOLDER_ENV_DEV_HOST':
33
+ return development
34
+ case 'PLACEHOLDER_ENV_PREPROD_HOST':
35
+ return preprod
36
+ case 'PLACEHOLDER_ENV_PROD_HOST':
37
+ return production
38
+ default:
39
+ return local
40
+ }
41
+ }
42
+
43
+ const env = getEnvironment()
44
+
45
+ export default env
@@ -0,0 +1,11 @@
1
+ import { createI18n } from 'vue-i18n'
2
+ import sv from './lang/sv.json'
3
+
4
+ const i18n = createI18n({
5
+ legacy: false,
6
+ locale: 'sv',
7
+ fallbackLocale: 'sv',
8
+ messages: { sv },
9
+ })
10
+
11
+ export default i18n
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,29 @@
1
+ import { VueQueryPlugin } from '@tanstack/vue-query'
2
+ import FDSVueCore from 'fds-vue-core'
3
+ import { createPinia } from 'pinia'
4
+ import { createApp } from 'vue'
5
+ import { queryClient } from './api/client'
6
+ import { setApiBaseUrlFromConfig } from './api/runtimeConfig'
7
+ import App from './App.vue'
8
+ import i18n from './i18n'
9
+ import router from './router'
10
+ import './style.css'
11
+
12
+ const app = createApp(App)
13
+ const pinia = createPinia()
14
+
15
+ async function bootstrap() {
16
+ const response = await fetch(`${import.meta.env.BASE_URL}config.json`)
17
+ const config = (await response.json()) as Record<string, unknown>
18
+ setApiBaseUrlFromConfig(config)
19
+
20
+ app
21
+ .use(pinia)
22
+ .use(router)
23
+ .use(i18n)
24
+ .use(VueQueryPlugin, { queryClient })
25
+ .use(FDSVueCore)
26
+ app.mount('#app')
27
+ }
28
+
29
+ void bootstrap()
File without changes