diff --git a/common/helpers/endpoints.d.ts b/common/helpers/endpoints.d.ts new file mode 100644 index 0000000000..213a894369 --- /dev/null +++ b/common/helpers/endpoints.d.ts @@ -0,0 +1,402 @@ +import type { + CollectionReference, + ResourceInterface, + ResourceReference, + HalJsonVuex, +} from 'hal-json-vuex' + +interface UserEntity extends ResourceInterface { + id: string + displayName: string + + profile: ResourceReference +} + +interface ProfileEntity extends ResourceInterface { + id: string + firstname: string + surname: string + nickname: string + legalName: string + + email: string + + language: string + + user: ResourceReference +} + +interface CampEntity extends ResourceInterface { + id: string + name: string + title: string + motto: string + + isPrototype: boolean + creator: ResourceReference + + addressName: string | null + addressStreet: string | null + addressZipcode: string | null + addressCity: string | null + + coachName: string | null + courseKind: string | null + courseNumber: string | null + organizer: string | null + kind: string | null + printYSLogoOnPicasso: boolean | null + trainingAdvisorName: string | null + + activities: CollectionReference + periods: CollectionReference + categories: CollectionReference + profiles: CollectionReference + progressLabels: CollectionReference +} + +interface PeriodEntity extends ResourceInterface { + id: string + start: string + end: string + + camp: ResourceReference +} + +interface ActivityEntity extends ResourceInterface { + id: string + title: string + location: string + + camp: ResourceReference + category: ResourceReference + period: ResourceReference + scheduleEntries: CollectionReference + activityResponsibles: CollectionReference + activityProgressLabel: ResourceReference +} + +interface ActivityProgressLabelEntity + extends ResourceInterface { + id: string + title: string + position: number + camp: ResourceReference +} + +interface ActivityResponsibleEntity extends ResourceInterface { + id: string + activity: ResourceReference + campCollaboration: ResourceReference +} + +interface ScheduleEntryEntity extends ResourceInterface { + id: string + start: string + end: string + + dayNumber: number + scheduleEntryNumber: number + number: string + + left: number + width: number + + period: ResourceReference + day: ResourceReference +} + +interface DayEntity extends ResourceInterface { + id: string + start: string + end: string + + number: number + dayOffset: number + + period: ResourceReference + scheduleEntries: CollectionReference + dayResponsibles: CollectionReference +} + +interface DayResponsibleEntity extends ResourceInterface { + id: string + day: ResourceReference + campCollaboration: ResourceReference +} + +interface CategoryEntity extends ResourceInterface { + id: string + short: string + name: string + + numberingStyle: 'a' | 'A' | 'i' | 'I' | '1' + color: string + + camp: ResourceReference + contentNodes: CollectionReference + rootContentNode: ResourceReference + preferredContentNodes: CollectionReference +} + +type ContentNode = + | ColumnLayoutNodeEntity + | MultiSelectNodeEntity + | SingleTextNodeEntity + | StoryboardNodeEntity + +interface ContentNodesBase { + id: string + contentTypeName: string + instanceName: string | null + slot: string + position: number + data: Data + + contentType: ResourceReference + + children: CollectionReference + parent: ResourceReference + root: ResourceReference +} + +interface ColumnLayoutNodeData { + columns: { + slot: string + width: number + }[] +} + +interface ColumnLayoutNodeEntity + extends ContentNodesBase, + ResourceInterface {} + +interface MultiSelectNodeData { + options: { + [key: string]: { + checked: boolean + } + } +} + +interface MultiSelectNodeEntity + extends ContentNodesBase, + ResourceInterface {} + +interface SingleTextNodeData { + html: string +} + +interface SingleTextNodeEntity + extends ContentNodesBase, + ResourceInterface {} + +interface StoryboardNodeData { + sections: { + [key: string]: { + column1: string + column2Html: string + column3: string + position: number + } + } +} + +interface StoryboardNodeEntity + extends ContentNodesBase, + ResourceInterface {} + +interface MaterialNodeEntity + extends ContentNodesBase, + ResourceInterface { + materialItems: CollectionReference +} + +interface ContentTypeEntity extends ResourceInterface { + id: string + name: string + + contentNodes: CollectionReference +} + +interface CampCollaborationEntity extends ResourceInterface { + id: string + role: 'member' | 'manager' | 'guest' + status: 'invited' | 'established' | 'inactive' + camp: ResourceReference + + inviteEmail: string | null + user: ResourceReference | null +} + +interface MaterialListEntity extends ResourceInterface { + id: string + name: string + + itemCount: number + camp: ResourceReference + campCollaboration: ResourceReference +} + +interface MaterialItemBase { + id: string + article: string + quantity: number + unit: string + materialList: ResourceReference +} + +type MaterialItemEntity = MaterialItemNodeEntity | MaterialItemPeriodEntity + +interface MaterialItemNodeEntity + extends MaterialItemBase, + ResourceInterface { + materialNode: ResourceReference + period: null +} + +interface MaterialItemPeriodEntity + extends MaterialItemBase, + ResourceInterface { + materialNode: null + period: ResourceReference +} + +interface InvitationDTO extends ResourceInterface { + campId: string + campTitle: string + userDisplayName: string | null + userAlreadyInCamp: boolean | null +} + +interface InvitationDTOParams { + action: 'find' | 'accept' | 'reject' + id: string +} + +type CampParam = { camp?: string | string[] } +type ActivityParam = { activity?: string | string[] } +type PeriodParam = { period?: string | string[] } +type ActivityResponsiblesParams = ActivityParam & { + activity?: { camp: string | string[] } +} +type ActivityResponsibleParams = { + activityResponsibles?: ActivityParam +} +type CampPrototypeQueryParam = { isPrototype: boolean } +type ContentNodeParam = { + contentType?: string | string[] + root?: string | string[] + period?: string +} +type CategoryParam = { categories?: string | string[] } +type DayParam = { day?: string | string[] } +type DayResponsibleParams = DayParam & { + day?: { period: string | string[] } +} +type MaterialListParam = { materialList?: string | string[] } +type MaterialNodeParam = { materialNode?: string | string[] } +type MaterialItemParams = MaterialListParam & MaterialNodeParam & { period?: string } +type ProfileParams = { + user: { collaborations: { camp: string | string[] } } +} +type TimeParam = { + before?: string + strictly_before?: string + after?: string + strictly_after?: string +} +type ScheduleEntryParams = PeriodParam & + ActivityParam & { + start?: TimeParam + end?: TimeParam +} + +type SingleResource> = ResourceReference +type QueryResources< + T extends ResourceInterface, + Params = undefined, +> = CollectionReference + +export interface RootEndpoint extends ResourceInterface { + activities: QueryResources & SingleResource + + activityProgressLabels: QueryResources & + SingleResource + + activityResponsibles: QueryResources< + ActivityResponsibleEntity, + ActivityResponsiblesParams + > & + SingleResource + + campCollaborations: QueryResources< + CampCollaborationEntity, + CampParam & ActivityResponsibleParams + > & + SingleResource + + camps: QueryResources & SingleResource + + categories: QueryResources & SingleResource + + columnLayouts: QueryResources & + SingleResource + + contentNodes: CollectionReference + + contentTypes: QueryResources & + SingleResource + + days: CollectionReference | SingleResource + + dayResponsibles: QueryResources & + SingleResource + + invitations: ResourceReference + + login: ResourceReference + + materialItems: QueryResources & + SingleResource + + materialLists: QueryResources & + SingleResource + + materialNodes: QueryResources & + SingleResource + + multiSelects: QueryResources & + SingleResource + + oauthCevidb: ResourceReference + + oauthGoogle: ResourceReference + + oauthJubladb: ResourceReference + + oauthPbsmidata: ResourceReference + + periods: QueryResources & SingleResource + + profiles: QueryResources & SingleResource + + resetPassword: ResourceReference + + scheduleEntries: QueryResources & + SingleResource + + singleTexts: QueryResources & + SingleResource + + storyboards: QueryResources & + SingleResource + + users: CollectionReference & SingleResource +} + +declare module 'vue/types/vue' { + interface Vue { + api: HalJsonVuex + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 0830ee320a..804abe537b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -159,7 +159,7 @@ services: - FUNCTION_ENABLE_INCOGNITO_MODE=true e2e: - image: cypress/included:cypress-13.13.0-node-20.15.1-chrome-126.0.6478.114-1-ff-128.0-edge-126.0.2592.61-1@sha256:f9733a2cadc3aa270e40f8ce1158a23cb99703476a9db7154b4ecc51ba02bd5c + image: cypress/included:cypress-13.13.3-node-22.7.0-chrome-127.0.6533.119-1-ff-129.0.1-edge-127.0.2651.98-1@sha256:82261c30eed7477e5a50e9579e5d251c4728d4e2f9eddfab57911e4b62de6444 profiles: ['e2e'] container_name: 'ecamp3-e2e' environment: @@ -170,7 +170,7 @@ services: - /tmp/.X11-unix:/tmp/.X11-unix:rw network_mode: host working_dir: /e2e - + reverse-proxy: image: nginx:1.27 container_name: 'ecamp3-reverse-proxy' diff --git a/frontend/.gitignore b/frontend/.gitignore index 3c17c4e931..6bcea5623c 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -11,6 +11,7 @@ selenium-debug.log # Auto-generated files public/twemoji/ +components.d.ts # Log files npm-debug.log* diff --git a/frontend/env.d.ts b/frontend/env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/frontend/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6b95e67f88..528fd4ab7a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -47,7 +47,7 @@ "deepmerge": "4.3.1", "emoji-regex": "10.4.0", "file-saver": "2.0.5", - "hal-json-vuex": "2.0.0-alpha.16", + "hal-json-vuex": "3.0.0-alpha.8", "inter-ui": "3.19.3", "js-cookie": "3.0.5", "linkify-it": "5.0.0", @@ -83,7 +83,9 @@ "@vitest/coverage-v8": "2.1.3", "@vue/babel-preset-app": "5.0.8", "@vue/eslint-config-prettier": "9.0.0", + "@vue/eslint-config-typescript": "^14.0.0-rc.1", "@vue/test-utils": "1.3.6", + "@vue/tsconfig": "^0.1.3", "autoprefixer": "10.4.20", "babel-plugin-require-context-hook": "1.0.0", "eslint": "9.13.0", @@ -101,13 +103,15 @@ "lint-staged": "15.2.10", "prettier": "3.3.3", "sass": "1.32.13", + "typescript": "5.6.2", "unplugin-vue-components": "0.27.4", "vite": "5.4.9", "vite-plugin-comlink": "5.0.1", "vite-plugin-vue2-svg": "0.4.0", "vitest": "2.1.3", "vitest-canvas-mock": "0.3.3", - "vue-template-compiler": "2.7.15" + "vue-template-compiler": "2.7.15", + "vue-tsc": "^0.38.8" } }, "node_modules/@adobe/css-tools": { @@ -4114,6 +4118,236 @@ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.7.0.tgz", + "integrity": "sha512-RIHOoznhA3CCfSTFiB6kBGLQtB/sox+pJ6jeFu6FxJvqL8qRxq/FfGO/UhsGgQM9oGdXkV4xUgli+dt26biB6A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.7.0", + "@typescript-eslint/type-utils": "8.7.0", + "@typescript-eslint/utils": "8.7.0", + "@typescript-eslint/visitor-keys": "8.7.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.7.0.tgz", + "integrity": "sha512-lN0btVpj2unxHlNYLI//BQ7nzbMJYBVQX5+pbNXvGYazdlgYonMn4AhhHifQ+J4fGRYA/m1DjaQjx+fDetqBOQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.7.0", + "@typescript-eslint/types": "8.7.0", + "@typescript-eslint/typescript-estree": "8.7.0", + "@typescript-eslint/visitor-keys": "8.7.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz", + "integrity": "sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.7.0", + "@typescript-eslint/visitor-keys": "8.7.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.7.0.tgz", + "integrity": "sha512-tl0N0Mj3hMSkEYhLkjREp54OSb/FI6qyCzfiiclvJvOqre6hsZTGSnHtmFLDU8TIM62G7ygEa1bI08lcuRwEnQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.7.0", + "@typescript-eslint/utils": "8.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.7.0.tgz", + "integrity": "sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz", + "integrity": "sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.7.0", + "@typescript-eslint/visitor-keys": "8.7.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.7.0.tgz", + "integrity": "sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.7.0", + "@typescript-eslint/types": "8.7.0", + "@typescript-eslint/typescript-estree": "8.7.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz", + "integrity": "sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.7.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitejs/plugin-vue2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.3.1.tgz", @@ -4315,6 +4549,49 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@volar/code-gen": { + "version": "0.38.9", + "resolved": "https://registry.npmjs.org/@volar/code-gen/-/code-gen-0.38.9.tgz", + "integrity": "sha512-n6LClucfA+37rQeskvh9vDoZV1VvCVNy++MAPKj2dT4FT+Fbmty/SDQqnsEBtdEe6E3OQctFvA/IcKsx3Mns0A==", + "dev": true, + "dependencies": { + "@volar/source-map": "0.38.9" + } + }, + "node_modules/@volar/source-map": { + "version": "0.38.9", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-0.38.9.tgz", + "integrity": "sha512-ba0UFoHDYry+vwKdgkWJ6xlQT+8TFtZg1zj9tSjj4PykW1JZDuM0xplMotLun4h3YOoYfY9K1huY5gvxmrNLIw==", + "dev": true + }, + "node_modules/@volar/vue-code-gen": { + "version": "0.38.9", + "resolved": "https://registry.npmjs.org/@volar/vue-code-gen/-/vue-code-gen-0.38.9.tgz", + "integrity": "sha512-tzj7AoarFBKl7e41MR006ncrEmNPHALuk8aG4WdDIaG387X5//5KhWC5Ff3ZfB2InGSeNT+CVUd74M0gS20rjA==", + "deprecated": "WARNING: This project has been renamed to @vue/language-core. Install using @vue/language-core instead.", + "dev": true, + "dependencies": { + "@volar/code-gen": "0.38.9", + "@volar/source-map": "0.38.9", + "@vue/compiler-core": "^3.2.37", + "@vue/compiler-dom": "^3.2.37", + "@vue/shared": "^3.2.37" + } + }, + "node_modules/@volar/vue-typescript": { + "version": "0.38.9", + "resolved": "https://registry.npmjs.org/@volar/vue-typescript/-/vue-typescript-0.38.9.tgz", + "integrity": "sha512-iJMQGU91ADi98u8V1vXd2UBmELDAaeSP0ZJaFjwosClQdKlJQYc6MlxxKfXBZisHqfbhdtrGRyaryulnYtliZw==", + "deprecated": "WARNING: This project has been renamed to @vue/typescript. Install using @vue/typescript instead.", + "dev": true, + "dependencies": { + "@volar/code-gen": "0.38.9", + "@volar/source-map": "0.38.9", + "@volar/vue-code-gen": "0.38.9", + "@vue/compiler-sfc": "^3.2.37", + "@vue/reactivity": "^3.2.37" + } + }, "node_modules/@vue/babel-helper-vue-jsx-merge-props": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", @@ -4742,6 +5019,46 @@ "prettier": ">= 3.0.0" } }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.0.0-rc.1", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.0.0-rc.1.tgz", + "integrity": "sha512-RMSEN2DuVvZa3W0ySN7aUm9xxCTGfCUIxDcqj9OnMkG9l1ugoKbf+a+yhazUzzUX4P96Z+l67uaFRYwV0eDXUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", + "typescript-eslint": "^8.6.0", + "vue-eslint-parser": "^9.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.10.tgz", + "integrity": "sha512-kW08v06F6xPSHhid9DJ9YjOGmwNDOsJJQk0ax21wKaUYzzuJGEuoKNU2Ujux8FLMrP7CFJJKsHhXN9l2WOVi2g==", + "dev": true, + "dependencies": { + "@vue/shared": "3.5.10" + } + }, + "node_modules/@vue/reactivity/node_modules/@vue/shared": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.10.tgz", + "integrity": "sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==", + "dev": true + }, "node_modules/@vue/shared": { "version": "3.5.12", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", @@ -4765,6 +5082,20 @@ "vue-template-compiler": "^2.x" } }, + "node_modules/@vue/tsconfig": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.1.3.tgz", + "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==", + "dev": true, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@zxcvbn-ts/core": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-3.0.4.tgz", @@ -7401,36 +7732,69 @@ "dev": true, "license": "ISC" }, - "node_modules/hal-json-normalizer": { + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/hal-json-normalizer-esm": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hal-json-normalizer/-/hal-json-normalizer-4.2.0.tgz", - "integrity": "sha512-TnlN4OzP/RPTCRC/+Zy9qjt9mefSHEjQrI2RQPERahBsOh5gPnUnpuH2DfIM3NiMdlCNQaTQuyhU1oJ0L4vNEg==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/hal-json-normalizer-esm/-/hal-json-normalizer-esm-4.2.0.tgz", + "integrity": "sha512-me5ykuWIeVbK8QQkY74cafZmylVPbBEV2loslrsjRo4dL++jk2hdtHPPwpAg/8bx0xbsC8QfuBNzozdUfTuRgA==", "dependencies": { - "lodash": "^4.17.15" + "lodash-es": "^4.17.21" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 18.17.0" } }, "node_modules/hal-json-vuex": { - "version": "2.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/hal-json-vuex/-/hal-json-vuex-2.0.0-alpha.16.tgz", - "integrity": "sha512-7OtQtJLr9Od4giw27ryINOYlM/6dvNZlcQiiseINq8qKrpc5dV9hLo4QOvKy8YGgnQOXYnCMSJ55/7XM+qiPGg==", - "license": "MIT", + "version": "3.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/hal-json-vuex/-/hal-json-vuex-3.0.0-alpha.8.tgz", + "integrity": "sha512-0u2vcu7XT4t+53esHQaFq4x3ArBTJzsZ6eqDGz3t3Z8ooPyyoTUj/I0IOMEBa5RgSuoVKSP3mswpq3HmdaGwwg==", "dependencies": { - "hal-json-normalizer": "^4.2.0", - "url-template": "^2.0.8" + "hal-json-normalizer-esm": "^4.2.0", + "url-template": "^3.1.1", + "vue-demi": "^0.14.10" }, "engines": { - "node": ">=14.0.0 <19.0.0" + "node": ">=18.17.0" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } } }, - "node_modules/hal-json-vuex/node_modules/url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", - "license": "BSD" + "node_modules/hal-json-vuex/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } }, "node_modules/has-bigints": { "version": "1.0.2", @@ -8715,6 +9079,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11227,6 +11596,18 @@ "node": ">=18" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tslib": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", @@ -11259,6 +11640,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.7.0.tgz", + "integrity": "sha512-nEHbEYJyHwsuf7c3V3RS7Saq+1+la3i0ieR3qP0yjqWSzVmh8Drp47uOl9LjbPANac4S7EFSqvcYIKXUUwIfIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.7.0", + "@typescript-eslint/parser": "8.7.0", + "@typescript-eslint/utils": "8.7.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -12003,6 +12420,21 @@ "vue": "^2.6 || ^3.2" } }, + "node_modules/vue-tsc": { + "version": "0.38.9", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-0.38.9.tgz", + "integrity": "sha512-Yoy5phgvGqyF98Fb4mYqboR4Q149jrdcGv5kSmufXJUq++RZJ2iMVG0g6zl+v3t4ORVWkQmRpsV4x2szufZ0LQ==", + "dev": true, + "dependencies": { + "@volar/vue-typescript": "0.38.9" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/vue/node_modules/@vue/compiler-sfc": { "version": "2.7.15", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz", diff --git a/frontend/package.json b/frontend/package.json index c6e66c716c..2ac01d9109 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -59,7 +59,7 @@ "deepmerge": "4.3.1", "emoji-regex": "10.4.0", "file-saver": "2.0.5", - "hal-json-vuex": "2.0.0-alpha.16", + "hal-json-vuex": "3.0.0-alpha.8", "inter-ui": "3.19.3", "js-cookie": "3.0.5", "linkify-it": "5.0.0", @@ -95,7 +95,9 @@ "@vitest/coverage-v8": "2.1.3", "@vue/babel-preset-app": "5.0.8", "@vue/eslint-config-prettier": "9.0.0", + "@vue/eslint-config-typescript": "^14.0.0-rc.1", "@vue/test-utils": "1.3.6", + "@vue/tsconfig": "^0.1.3", "autoprefixer": "10.4.20", "babel-plugin-require-context-hook": "1.0.0", "eslint": "9.13.0", @@ -113,13 +115,15 @@ "lint-staged": "15.2.10", "prettier": "3.3.3", "sass": "1.32.13", + "typescript": "5.6.2", "unplugin-vue-components": "0.27.4", "vite": "5.4.9", "vite-plugin-comlink": "5.0.1", "vite-plugin-vue2-svg": "0.4.0", "vitest": "2.1.3", "vitest-canvas-mock": "0.3.3", - "vue-template-compiler": "2.7.15" + "vue-template-compiler": "2.7.15", + "vue-tsc": "^0.38.8" }, "overrides": { "uri-js": "npm:uri-js-replace" diff --git a/frontend/src/plugins/store/index.js b/frontend/src/plugins/store/index.js index cc7c66358d..993281bf56 100644 --- a/frontend/src/plugins/store/index.js +++ b/frontend/src/plugins/store/index.js @@ -1,7 +1,7 @@ import Vuex from 'vuex' import axios from 'axios' import VueAxios from 'vue-axios/dist/vue-axios.common.min' -import HalJsonVuex from 'hal-json-vuex' +import { HalJsonVuexPlugin } from 'hal-json-vuex' import lang from './lang' import auth from './auth' import preferences from './preferences' @@ -32,11 +32,10 @@ class StorePlugin { Vue.use(VueAxios, axios) - let halJsonVuex = HalJsonVuex - if (typeof halJsonVuex !== 'function') { - halJsonVuex = HalJsonVuex.default - } - apiStore = halJsonVuex(store, axios, { forceRequestedSelfLink: true }) + /** + * @type apiStore {import('hal-json-vuex').HalJsonVuex} + */ + apiStore = HalJsonVuexPlugin(store, axios, { forceRequestedSelfLink: true }) Vue.use(apiStore) } } diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000000..cdbea1d76e --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.web.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/frontend/tsconfig.config.json b/frontend/tsconfig.config.json new file mode 100644 index 0000000000..c2d3a309ef --- /dev/null +++ b/frontend/tsconfig.config.json @@ -0,0 +1,8 @@ +{ + "extends": "@vue/tsconfig/tsconfig.node.json", + "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"], + "compilerOptions": { + "composite": true, + "types": ["node"] + } +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000000..31f90037cb --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.config.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/frontend/tsconfig.vitest.json b/frontend/tsconfig.vitest.json new file mode 100644 index 0000000000..d080d611e3 --- /dev/null +++ b/frontend/tsconfig.vitest.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.app.json", + "exclude": [], + "compilerOptions": { + "composite": true, + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/print/components/generic/ErrorMessage.vue b/print/components/generic/ErrorMessage.vue index 273f7935b2..e11b4b305f 100644 --- a/print/components/generic/ErrorMessage.vue +++ b/print/components/generic/ErrorMessage.vue @@ -3,7 +3,8 @@

{{ title }}

- {{ error }} + {{ error.message }}
+ {{ error.stack }} diff --git a/print/components/scheduleEntry/contentNode/ResponsiveLayout.vue b/print/components/scheduleEntry/contentNode/ResponsiveLayout.vue index ead56e9df6..53a8ae9014 100644 --- a/print/components/scheduleEntry/contentNode/ResponsiveLayout.vue +++ b/print/components/scheduleEntry/contentNode/ResponsiveLayout.vue @@ -1,8 +1,16 @@ diff --git a/print/nuxt.config.js b/print/nuxt.config.js index 38aa28a265..9e0e4d291a 100644 --- a/print/nuxt.config.js +++ b/print/nuxt.config.js @@ -11,7 +11,7 @@ export default defineNuxtConfig({ '~/assets/calendar/CalendarDaily.sass', '~/assets/calendar/CalendarWithEvents.sass', ], - plugins: [{ src: '~/plugins/hal-json-vuex.js' }, { src: '~/plugins/dayjs.js' }], + plugins: [{ src: '~/plugins/hal-json-vuex.ts' }, { src: '~/plugins/dayjs.js' }], components: [ { path: '~/components/config', prefix: 'Config', global: 'true' }, @@ -80,7 +80,6 @@ export default defineNuxtConfig({ }, telemetry: false, - vite: { optimizeDeps: { include: [ diff --git a/print/package-lock.json b/print/package-lock.json index 480345dec0..0ba573d707 100644 --- a/print/package-lock.json +++ b/print/package-lock.json @@ -13,11 +13,13 @@ "colorjs.io": "0.5.2", "dayjs": "1.11.13", "deepmerge": "4.3.1", - "hal-json-vuex": "3.0.0-alpha.1", + "hal-json-normalizer-esm": "4.2.0", + "hal-json-vuex": "3.0.0-alpha.8", "isomorphic-dompurify": "2.16.0", "lodash": "4.17.21", "puppeteer-core": "23.6.0", "runes": "0.4.3", + "url-template": "3.1.1", "vuex": "4.1.0" }, "devDependencies": { @@ -9539,29 +9541,64 @@ "unenv": "^1.10.0" } }, - "node_modules/hal-json-normalizer": { + "node_modules/hal-json-normalizer-esm": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hal-json-normalizer/-/hal-json-normalizer-4.2.0.tgz", - "integrity": "sha512-TnlN4OzP/RPTCRC/+Zy9qjt9mefSHEjQrI2RQPERahBsOh5gPnUnpuH2DfIM3NiMdlCNQaTQuyhU1oJ0L4vNEg==", + "resolved": "https://registry.npmjs.org/hal-json-normalizer-esm/-/hal-json-normalizer-esm-4.2.0.tgz", + "integrity": "sha512-me5ykuWIeVbK8QQkY74cafZmylVPbBEV2loslrsjRo4dL++jk2hdtHPPwpAg/8bx0xbsC8QfuBNzozdUfTuRgA==", "license": "MIT", "dependencies": { - "lodash": "^4.17.15" + "lodash-es": "^4.17.21" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 18.17.0" } }, "node_modules/hal-json-vuex": { - "version": "3.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/hal-json-vuex/-/hal-json-vuex-3.0.0-alpha.1.tgz", - "integrity": "sha512-9aLwBtdNGxNGmJrDu/aEDTaCNgHMhiPTKE40iL7R08kYiVrf2VfV+jfu89GyJKYDgZiBjm46naQ9FmW0fnAAPQ==", + "version": "3.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/hal-json-vuex/-/hal-json-vuex-3.0.0-alpha.8.tgz", + "integrity": "sha512-0u2vcu7XT4t+53esHQaFq4x3ArBTJzsZ6eqDGz3t3Z8ooPyyoTUj/I0IOMEBa5RgSuoVKSP3mswpq3HmdaGwwg==", "license": "MIT", "dependencies": { - "hal-json-normalizer": "^4.2.0", - "url-template": "^2.0.8" + "hal-json-normalizer-esm": "^4.2.0", + "url-template": "^3.1.1", + "vue-demi": "^0.14.10" }, "engines": { - "node": ">=16.0.0 <21.0.0" + "node": ">=18.17.0" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/hal-json-vuex/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } } }, "node_modules/has-flag": { @@ -11031,6 +11068,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -17567,10 +17610,12 @@ "license": "MIT" }, "node_modules/url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", - "license": "BSD" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-3.1.1.tgz", + "integrity": "sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, "node_modules/urlpattern-polyfill": { "version": "8.0.2", diff --git a/print/package.json b/print/package.json index bbf65f6a2b..534c486cc3 100644 --- a/print/package.json +++ b/print/package.json @@ -22,11 +22,13 @@ "colorjs.io": "0.5.2", "dayjs": "1.11.13", "deepmerge": "4.3.1", - "hal-json-vuex": "3.0.0-alpha.1", + "hal-json-normalizer-esm": "4.2.0", + "hal-json-vuex": "3.0.0-alpha.8", "isomorphic-dompurify": "2.16.0", "lodash": "4.17.21", "puppeteer-core": "23.6.0", "runes": "0.4.3", + "url-template": "3.1.1", "vuex": "4.1.0" }, "devDependencies": { diff --git a/print/plugins/hal-json-vuex.ts b/print/plugins/hal-json-vuex.ts new file mode 100644 index 0000000000..2f617e5d5b --- /dev/null +++ b/print/plugins/hal-json-vuex.ts @@ -0,0 +1,41 @@ +import { HalJsonVuexPlugin } from 'hal-json-vuex' +import axios from 'axios' +import { createStore } from 'vuex' +import { + addAuthorizationInterceptor, + addDebugInterceptor, + addErrorLogInterceptor, +} from '~/plugins/hal-json-vuex/axios' +import type { RootEndpoint } from '~/common/helpers/endpoints' + +export default defineNuxtPlugin((nuxtApp) => { + // create store + const store = createStore({ + state() { + return {} + }, + }) + nuxtApp.vueApp.use(store) + + // create axios instance + const { internalApiRootUrl } = useRuntimeConfig() + const axiosInstance = axios.create({ + withCredentials: true, + baseURL: internalApiRootUrl, + headers: { common: { Accept: 'application/hal+json' } }, + }) + addAuthorizationInterceptor(axiosInstance) + addDebugInterceptor(axiosInstance) + addErrorLogInterceptor(axiosInstance) + + // create and inject API + const api = HalJsonVuexPlugin(store, axiosInstance, { + forceRequestedSelfLink: true, + }) + + return { + provide: { + api, + }, + } +}) diff --git a/print/plugins/hal-json-vuex.js b/print/plugins/hal-json-vuex/axios.js similarity index 59% rename from print/plugins/hal-json-vuex.js rename to print/plugins/hal-json-vuex/axios.js index cc28e38575..0d5586bff5 100644 --- a/print/plugins/hal-json-vuex.js +++ b/print/plugins/hal-json-vuex/axios.js @@ -1,40 +1,4 @@ -import HalJsonVuex from 'hal-json-vuex' -import axios from 'axios' -import { createStore } from 'vuex' - -export default defineNuxtPlugin((nuxtApp) => { - // create store - const store = createStore({ - state() { - return {} - }, - }) - nuxtApp.vueApp.use(store) - - // create axios instance - const { internalApiRootUrl } = useRuntimeConfig() - const axiosInstance = axios.create({ - withCredentials: true, - baseURL: internalApiRootUrl, - headers: { common: { Accept: 'application/hal+json' } }, - }) - addAuthorizationInterceptor(axiosInstance) - addDebugInterceptor(axiosInstance) - addErrorLogInterceptor(axiosInstance) - - // create and inject API - const api = new HalJsonVuex(store, axiosInstance, { - forceRequestedSelfLink: true, - }) - - return { - provide: { - api, - }, - } -}) - -function addAuthorizationInterceptor(axios) { +export function addAuthorizationInterceptor(axios) { const { basicAuthToken } = useRuntimeConfig() const requestHeaders = useRequestHeaders(['cookie']) @@ -58,7 +22,7 @@ function addAuthorizationInterceptor(axios) { }) } -function addDebugInterceptor(axios) { +export function addDebugInterceptor(axios) { if (!import.meta.env.DEV) { return } @@ -81,7 +45,7 @@ function addDebugInterceptor(axios) { }) } -function addErrorLogInterceptor(axios) { +export function addErrorLogInterceptor(axios) { axios.interceptors.response.use( (response) => response, (error) => {