diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..50bd20b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +.stamps +.env +.env.* +*.local +docs diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..47039fa --- /dev/null +++ b/.env.production @@ -0,0 +1,2 @@ + +VITE_TANSTACK_DEVTOOLS=false diff --git a/.gitignore b/.gitignore index 8b25bb5..ce97652 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,6 @@ dist dist-ssr *.local .env -.nitro -.tanstack -.wrangler -.output -.vinxi __unconfig* todos.json +.stamps/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..21487e4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:stable-alpine +COPY ./dist/. /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84340d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +IMAGE_NAME=by-other-means +DOCKER_REGISTRY=gitea.pixelparasol.com +DOCKER_WORKSPACE=nathan +DOCKER_TARGET=$(DOCKER_REGISTRY)/$(DOCKER_WORKSPACE)/$(IMAGE_NAME) +K8S_NAMESPACE=by-other-means +TAG := $(shell git rev-parse --short HEAD) +STAMPS := .stamps + +.PHONY: all +all: + @echo "Available targets:" + @echo " npm-install - Force a clean reinstall of npm dependencies" + @echo " npm-build - Build the project (skipped if sources unchanged)" + @echo " docker-build - Build the Docker image (skipped if unchanged)" + @echo " docker-push - Push the Docker image (skipped if already pushed for current commit)" + @echo " helm - Deploy or upgrade the Helm chart (skipped if charts and image unchanged)" + +# Force a clean reinstall (explicit use only) +.PHONY: npm-install +npm-install: + @echo "📦 Installing npm dependencies" + @rm -rf node_modules + @npm ci + +# Smart install: only runs if package files changed +node_modules/.package-lock.json: package-lock.json package.json + @echo "📦 Installing npm dependencies" + @npm ci + +$(STAMPS): + @mkdir -p $@ + +# Smart build: only runs if source files or deps changed +SRC_FILES := $(shell find src -type f 2>/dev/null) +$(STAMPS)/npm-build: $(SRC_FILES) node_modules/.package-lock.json | $(STAMPS) + @echo "🏗️ Building the project" + @npm run build + @touch $@ + +.PHONY: npm-build +npm-build: $(STAMPS)/npm-build + +# Smart docker build: only runs if npm build or Dockerfile changed, per tag +$(STAMPS)/docker-build-$(TAG): $(STAMPS)/npm-build Dockerfile | $(STAMPS) + @echo "🐳 Building Docker image $(DOCKER_TARGET):$(TAG)" + @docker buildx build --no-cache -f Dockerfile . --platform linux/amd64 \ + --load \ + -t $(DOCKER_TARGET):$(TAG) \ + -t $(DOCKER_TARGET):latest + @touch $@ + +.PHONY: docker-build +docker-build: $(STAMPS)/docker-build-$(TAG) + +# Smart push: only pushes if docker image was (re)built and not yet pushed +$(STAMPS)/docker-pushed-$(TAG): $(STAMPS)/docker-build-$(TAG) | $(STAMPS) + @echo "🔐 Logging into Docker registry $(DOCKER_REGISTRY)" + @docker login $(DOCKER_REGISTRY) + @echo "🚀 Pushing Docker image $(DOCKER_TARGET):$(TAG) and latest" + @docker push $(DOCKER_TARGET):$(TAG) + @docker push $(DOCKER_TARGET):latest + @touch $@ + +.PHONY: docker-push +docker-push: $(STAMPS)/docker-pushed-$(TAG) + +# Smart helm deploy: only runs if chart files changed or a new image was pushed +HELM_FILES := $(shell find helm -type f 2>/dev/null) +$(STAMPS)/helm: $(HELM_FILES) $(STAMPS)/docker-pushed-$(TAG) | $(STAMPS) + @echo "🚀 Deploying Helm chart to namespace '$(K8S_NAMESPACE)'" + @helm upgrade --install $(K8S_NAMESPACE) ./helm -n $(K8S_NAMESPACE) --create-namespace + @touch $@ + +.PHONY: helm +helm: $(STAMPS)/helm diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 0000000..e477087 --- /dev/null +++ b/helm/.helmignore @@ -0,0 +1,30 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + + +# NOTES.txt +# deployment.yml +# secrets.yml +# service.yml +# persistent_volume.yml diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..dfeb230 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: helm +description: By Other Means Control Timer Helm Chart + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/helm/readme.md b/helm/readme.md new file mode 100644 index 0000000..2f2cc96 --- /dev/null +++ b/helm/readme.md @@ -0,0 +1,64 @@ +## Installation + +``` +helm upgrade --install by-other-means ./helm -n by-other-means --create-namespace +``` + +## Secrets + +Gitea Auth secret for pulling the container from the private registry (namespace-scoped, so this +needs to be created once per namespace): + +``` +kubectl -n by-other-means \ + create secret docker-registry gitea-auth \ + --docker-server=https://gitea.pixelparasol.com \ + --docker-username=nathan \ + --docker-password= \ + --docker-email=nathan@pixelparasol.com +``` + +## Build the image + +``` +npm run build + +DOCKER_REGISTRY="gitea.pixelparasol.com/nathan/by-other-means" && CI_COMMIT_SHORT_SHA=$(git rev-parse --short HEAD) + +docker buildx build --no-cache -f Dockerfile . --platform linux/amd64 -t $DOCKER_REGISTRY:latest -t $DOCKER_REGISTRY:$CI_COMMIT_SHORT_SHA + +docker login gitea.pixelparasol.com + +docker push $DOCKER_REGISTRY:$CI_COMMIT_SHORT_SHA; docker push $DOCKER_REGISTRY:latest +``` + +### Troubleshooting + +You may find yourself in a state where you need to do secret checks to see how they render. +adding the `--dry-run=server` argument to your upgrade will render the secret checks. + +``` +helm upgrade --install by-other-means ./helm -n by-other-means --dry-run=server +``` + +## Helm Diff + +Install the `helm-diff` plugin + +``` +helm plugin install https://github.com/databus23/helm-diff +``` + +view the diff if an upgrade were issued + +``` +helm diff upgrade by-other-means ./helm -n by-other-means +``` + +## Deployment + +The app can be deployed by tag by specifying it with `--set` + +``` +helm upgrade by-other-means ./helm -n by-other-means --set deploy.app.tag="v1.0.4" +``` diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt new file mode 100644 index 0000000..f901a7c --- /dev/null +++ b/helm/templates/NOTES.txt @@ -0,0 +1,23 @@ +1. {{ .Release.Name }} Deployment Information: + - Release Name: {{ .Release.Name }} + - Namespace: {{ .Release.Namespace }} + - Chart Name: {{ .Chart.Name }} + - Chart Version: {{ .Chart.Version }} + +2. {{ .Release.Name }} Service Information: + - Service Name: {{ .Values.system }}-service + - Service Type: LoadBalancer + - Service Port: 80 + +3. Useful Commands: + - Check the {{ .Release.Name }} Deployment Status: + helm status {{ .Release.Name }} + + - Get Detailed Information about the {{ .Release.Name }} Deployment: + helm get all {{ .Release.Name }} + + helm diff + +4. Clean Up: + - To uninstall/delete the {{ .Release.Name }} deployment, run: + helm uninstall {{ .Release.Name }} diff --git a/helm/templates/_deployment.tpl b/helm/templates/_deployment.tpl new file mode 100644 index 0000000..6aa7511 --- /dev/null +++ b/helm/templates/_deployment.tpl @@ -0,0 +1,60 @@ +{{- define "system.deployment.component" }} +{{ $image := .component.image }} +{{ $component := .component }} +{{ $release := .release }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .component.name }} + labels: +{{ include "helper.metaLabels" . | indent 4 }} +spec: + replicas: 1 + selector: + matchLabels: +{{ include "helper.matchLabels" . | indent 8 }} + template: + metadata: + labels: +{{ include "helper.metaLabels" . | indent 8 }} + spec: +{{- include "helper.affinity" . | indent 6 }} +{{- if $image.imagePullSecrets }} + imagePullSecrets: + - name: {{ toString $image.imagePullSecrets }} +{{- end }} +{{- if .component.volumes }} +{{- range $key, $volume := .component.volumes }} + volumes: + {{- if eq $volume.type "configMap" }} + - name: "{{ $volume.name }}" + configMap: + name: "{{ $volume.name}}" + {{- else }} + - name: "{{ $release.Name }}-{{ $component.name }}-pvc" + persistentVolumeClaim: + claimName: "{{ $release.Name }}-{{ $component.name }}-pvc" + {{- end }} +{{- end }} +{{- end }} + containers: + - name: {{ .component.name }} +{{ include "helper.deploy_tag" (dict "component" .component "values" .values "release" .release) | indent 10 }} +{{- if .component.deployment }} + {{- toYaml .component.deployment | trim | nindent 10 }} +{{- end }} +{{- if .component.volumes }} +{{- range $key, $volume := .component.volumes }} + volumeMounts: + {{- if eq $volume.type "configMap" }} + - name: {{ $volume.name }} + mountPath: {{ $volume.mountPath }} + subPath: {{ $volume.subPath }} + {{- else }} + - mountPath: {{ $volume.containerMountPath }} + name: "{{ $release.Name }}-{{ $component.name }}-pvc" + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..3435614 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,76 @@ +{{/* +match labels +*/}} +{{- define "helper.matchLabels" -}} +app: {{ .component.name }} +{{- end }} + +{{/* +meta labels +*/}} +{{- define "helper.metaLabels" -}} +app: {{ .component.name }} +tenant: {{ .values.system }} +{{- end }} + +{{/* +Node Affinity helper +*/}} +{{- define "helper.affinity" -}} +{{- if .component.affinity -}} +nodeName: {{ .component.affinity }} +{{- end -}} +{{- end -}} + +{{/* +Image name and tag helper +*/}} +{{- define "helper.image" -}} +image: "{{ .component.image.repository }}:{{ .component.image.tag }}" +{{- end -}} + +{{- /* # returns one of the following +# - image: current_release:current_tag from lookup +# - image: current_release:deploy_tag from image lookup + +# if this iteration of the loop has a .Values.deploy override tag +# - set $new_image to the override tag +# else +# - set $new_image to the current tag +*/ -}} +{{- define "helper.deploy_tag" -}} + + {{- $new_deployment := "" -}} + {{- $component := .component.name -}} + + {{- $deployment := (lookup "apps/v1" "Deployment" .release.Namespace $component) | default dict }} + {{- if $deployment -}} + {{- $targetContainerName := $component -}} + {{- $targetContainer := dict -}} + {{- range $deployment.spec.template.spec.containers }} + {{- if eq .name $targetContainerName }} + {{- $targetContainer = . -}} + {{- end -}} + {{- end -}} + + {{- range $key, $deploy := .values.deploy -}} + {{- if eq $key $component -}} + {{- $new_deployment = $deploy.tag -}} + {{- end -}} + {{- end -}} + + {{- if $new_deployment -}} +image: "{{ .component.image.repository }}:{{ $new_deployment }}" + {{- else -}} +image: {{ $targetContainer.image }} + {{- end -}} + {{- else -}} +image: "{{ .component.image.repository }}:{{ .component.image.tag }}" + {{- end -}} +{{- end -}} + + +{{- /* include "helper.var_dump" . */ -}} +{{- define "helper.var_dump" -}} +{{- . | mustToPrettyJson | printf "\n%s" | fail }} +{{- end -}} diff --git a/helm/templates/_persistent_volume.tpl b/helm/templates/_persistent_volume.tpl new file mode 100644 index 0000000..4d69be0 --- /dev/null +++ b/helm/templates/_persistent_volume.tpl @@ -0,0 +1,35 @@ +{{- define "system.persistent_volume.component" }} +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .release.Name }}-{{ .component.name }}-pv" + labels: +{{ include "helper.metaLabels" . | indent 4 }} +spec: + storageClassName: {{ .volume.storageClassName }} + persistentVolumeReclaimPolicy: {{ .volume.reclaimPolicy }} + capacity: + storage: {{ .volume.capacity.storage }} + accessModes: + {{ toYaml .volume.accessModes }} +{{- if .volume.hostPath }} + hostPath: + path: {{ .volume.hostPath }} +{{- end }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "{{ .release.Name }}-{{ .component.name }}-pvc" + labels: +{{ include "helper.metaLabels" . | indent 4 }} +spec: + storageClassName: {{ .volume.storageClassName }} + volumeName: "{{ .release.Name }}-{{ .component.name }}-pv" + accessModes: + {{ toYaml .volume.accessModes }} + resources: + requests: + storage: {{ .volume.capacity.storage }} +{{- end }} diff --git a/helm/templates/_service.tpl b/helm/templates/_service.tpl new file mode 100644 index 0000000..9fd3450 --- /dev/null +++ b/helm/templates/_service.tpl @@ -0,0 +1,21 @@ +{{- define "system.service.component" }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .component.name }}-service + labels: +{{ include "helper.metaLabels" . | indent 4 }} +spec: + type: {{ .component.service.type }} + selector: + app: {{ .component.name }} + ports: +{{- range .component.service.ports }} + - protocol: {{ .protocol }} + port: {{ .port }} + targetPort: {{ .targetPort }} + name: {{ .name }} +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/helm/templates/cronjobs.yaml b/helm/templates/cronjobs.yaml new file mode 100644 index 0000000..3059993 --- /dev/null +++ b/helm/templates/cronjobs.yaml @@ -0,0 +1,57 @@ +{{- define "system.backup.cronjob" -}} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ .system_name }}-{{ .backup.pvc_name }}-backup +spec: + schedule: {{ required "Values.backups.volumes[n].schedule required in crontab format" .backup.schedule }} + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + jobTemplate: + spec: + template: + spec: + restartPolicy: OnFailure + volumes: + - name: nfs-backup + nfs: + server: {{ required "Values.backups.volumes[n].nas_ip required to make nfs connection" .nas_ip }} + path: {{ required "Values.backups.volumes[n].nas_path required to store backup to a nfs connection" .backup.nas_path }} + + - name: {{ required "Values.backups.volumes[n].pvc_name required" .backup.pvc_name }} + persistentVolumeClaim: + claimName: {{ .backup.pvc_name }} + containers: + - name: config-backup + image: {{ .image | default "alpine" }} + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - date; + echo "Creating backup for {{ .system_name }}"; + echo -e "\nCreating {{ .system_name }} directory if it doesnt already exist"; + mkdir -p /nas/backup/{{ .system_name }}; + echo -e "\nProcessing {{ .backup.pvc_name }}"; + echo " - Creating backup of {{ .backup.pvc_name }} volume"; + tar -czf /nas/backup/{{ .system_name }}/{{ .backup.pvc_name }}_$(date +%Y-%m-%d).tar.gz /backup_source; + echo " - Enforcing backup retention policy for {{ .backup.pvc_name }} to latest {{ required "Values.backups.volumes[n].retention required as an integer" .backup.retention }}"; + ls -t /nas/backup/{{ required "Values.backups.system_name required" .system_name }}/{{ .backup.pvc_name }}_*.tar.gz | sort -r | tail -n +{{ add1 .backup.retention }} | xargs rm -f; + echo -e "\nCompleted backup of {{ .backup.pvc_name }}"; + date; + + volumeMounts: + - name: nfs-backup + mountPath: /nas/backup + - name: {{ .backup.pvc_name }} + mountPath: /backup_source +{{- end }} + +{{ if .Values.backups.enabled }} +{{- range $parent, $backup := .Values.backups.volumes -}} +{{- if $backup.enabled }} +{{ include "system.backup.cronjob" (dict "backup" $backup "image" $.Values.backups.image "nas_ip" $.Values.backups.nas_ip "system_name" $.Values.backups.system_name) }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/deployment.yml b/helm/templates/deployment.yml new file mode 100644 index 0000000..274ab61 --- /dev/null +++ b/helm/templates/deployment.yml @@ -0,0 +1,3 @@ +{{- range $system, $settings := .Values.components -}} +{{ include "system.deployment.component" (dict "component" $settings "values" $.Values "release" $.Release) }} +{{- end }} diff --git a/helm/templates/ingress.yml b/helm/templates/ingress.yml new file mode 100644 index 0000000..1bafdf8 --- /dev/null +++ b/helm/templates/ingress.yml @@ -0,0 +1,29 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.system }}-ingress + labels: + app: {{ .Values.system }} + tenant: {{ .Values.system }} + annotations: + cert-manager.io/cluster-issuer: lets-encrypt-prod + nginx.ingress.kubernetes.io/rewrite-target: / + +spec: + ingressClassName: {{ .Values.components.app.ingress.className }} + tls: + - hosts: + - "{{ .Values.components.app.ingress.domain }}" + secretName: {{ .Values.system }}-ingress-tls + rules: + - host: "{{ .Values.components.app.ingress.domain }}" + http: + paths: + - path: {{ .Values.components.app.ingress.path }} + pathType: Prefix + backend: + service: + name: {{ .Values.system }}-service + port: + number: {{ .Values.components.app.ingress.port }} diff --git a/helm/templates/nginx_configmap.yml b/helm/templates/nginx_configmap.yml new file mode 100644 index 0000000..1c18c43 --- /dev/null +++ b/helm/templates/nginx_configmap.yml @@ -0,0 +1,28 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-configmap + labels: + app: {{ .Values.system }} + tenant: {{ .Values.system }} +data: + default.conf: | + server { + listen 80; + listen [::]:80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri /index.html =404; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + } diff --git a/helm/templates/persistent_volume.yml b/helm/templates/persistent_volume.yml new file mode 100644 index 0000000..63ec99a --- /dev/null +++ b/helm/templates/persistent_volume.yml @@ -0,0 +1,9 @@ +{{- range $system, $settings := .Values.components -}} + {{ if $settings.volumes }} + {{- range $component, $volume := $settings.volumes -}} + {{- if ne $volume.type "configMap" -}} +{{ include "system.persistent_volume.component" (dict "component" $settings "volume" $volume "values" $.Values "release" $.Release) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/secrets.yml b/helm/templates/secrets.yml new file mode 100644 index 0000000..5ddb08c --- /dev/null +++ b/helm/templates/secrets.yml @@ -0,0 +1,33 @@ + +{{/* +Secret initializer and data maintainer +This helper will initialize the secret data with "initial" value if the secret is not found in the secret object. +IF the secret is found in the secret object, it will use the set value from the secret object thereby maintaining the secret data. +*/}} +{{- define "helper.secrets" -}} + {{- $secretObj := (lookup "v1" "Secret" .release.Namespace .component.name) | default dict }} + {{- range $key, $value := .component.secrets }} + {{- if empty $value -}} + {{- $secretData := (get $secretObj "data") | default dict }} + {{- $secret := (get $secretData $key) | default ("initial" | b64enc) }} + {{ $key }}: {{ $secret | quote }} + {{- else }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end -}} + {{- end }} +{{- end -}} + +{{- range $system, $settings := .Values.components -}} +{{ if $settings.secrets }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $settings.name }} + labels: +{{ include "helper.metaLabels" (dict "component" $settings "values" $.Values) | indent 4 }} +type: Opaque +data: +{{- include "helper.secrets" (dict "component" $settings "release" $.Release) }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/service.yml b/helm/templates/service.yml new file mode 100644 index 0000000..edc0f4b --- /dev/null +++ b/helm/templates/service.yml @@ -0,0 +1,5 @@ +{{- range $system, $settings := .Values.components -}} +{{ if $settings.service }} +{{ include "system.service.component" (dict "component" $settings "values" $.Values) }} +{{- end }} +{{- end }} diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..3470e56 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,43 @@ +system: by-other-means + +components: + app: + name: by-other-means + + affinity: null + + image: + repository: gitea.pixelparasol.com/nathan/by-other-means + tag: "latest" + imagePullPolicy: "Always" + imagePullSecrets: "gitea-auth" + + ingress: + tls: true + domain: bom.nathanbstanley.com + path: / + className: "nginx" + port: 80 + + service: + type: ClusterIP + ports: + - protocol: TCP + port: 80 + targetPort: 80 + name: http + + deployment: + resources: + limits: + memory: "128Mi" + cpu: "500m" + + volumes: + - name: nginx-configmap + type: configMap + mountPath: "/etc/nginx/conf.d/default.conf" + subPath: "default.conf" + +backups: + enabled: false diff --git a/index.html b/index.html new file mode 100644 index 0000000..da64509 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + By Other Means Control Timer + + +
+ + + diff --git a/package-lock.json b/package-lock.json index 37c0485..ff98876 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "@tanstack/react-devtools": "latest", "@tanstack/react-router": "latest", "@tanstack/react-router-devtools": "latest", - "@tanstack/react-start": "latest", "luxon": "^3.7.2", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -22,7 +21,7 @@ "devDependencies": { "@biomejs/biome": "2.4.5", "@tanstack/devtools-vite": "latest", - "@tanstack/router-cli": "^1.132.0", + "@tanstack/router-plugin": "^1.132.0", "@types/luxon": "^3.7.2", "@types/node": "^22.10.2", "@types/react": "^19.2.0", @@ -34,24 +33,11 @@ "vite": "^8.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/compat-data": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -61,6 +47,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -91,6 +78,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -105,6 +93,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", @@ -121,6 +110,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", @@ -137,6 +127,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -146,6 +137,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -159,6 +151,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -176,6 +169,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -185,6 +179,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -194,6 +189,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -203,6 +199,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -216,6 +213,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -241,6 +239,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -255,6 +254,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -269,6 +269,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -287,6 +288,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -301,6 +303,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -473,16 +476,6 @@ "node": ">=14.21.3" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", - "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -549,54 +542,6 @@ "@emnapi/runtime": "^2.0.0-alpha.3" } }, - "node_modules/@oozcitak/dom": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", - "integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==", - "license": "MIT", - "dependencies": { - "@oozcitak/infra": "^2.0.2", - "@oozcitak/url": "^3.0.0", - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/infra": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-2.0.2.tgz", - "integrity": "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==", - "license": "MIT", - "dependencies": { - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-3.0.0.tgz", - "integrity": "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==", - "license": "MIT", - "dependencies": { - "@oozcitak/infra": "^2.0.2", - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/util": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-10.0.0.tgz", - "integrity": "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==", - "license": "MIT", - "engines": { - "node": ">=20.0" - } - }, "node_modules/@oxc-parser/binding-android-arm-eabi": { "version": "0.120.0", "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.120.0.tgz", @@ -1549,6 +1494,47 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", @@ -1830,133 +1816,6 @@ } } }, - "node_modules/@tanstack/react-start": { - "version": "1.168.32", - "resolved": "https://registry.npmjs.org/@tanstack/react-start/-/react-start-1.168.32.tgz", - "integrity": "sha512-y1WXHo+jPfHxiuuN1m+br06IcriiBQnEWryBdbKdEOS5vw2PmnOj+Cgf1/YcGOqtSougScWFeE2rL1FXWvsLLg==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.18", - "@tanstack/react-start-client": "1.168.16", - "@tanstack/react-start-rsc": "0.1.31", - "@tanstack/react-start-server": "1.167.22", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-client-core": "1.170.14", - "@tanstack/start-plugin-core": "1.171.24", - "@tanstack/start-server-core": "1.169.17", - "pathe": "^2.0.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rsbuild/core": "^2.0.0", - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0", - "vite": ">=7.0.0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@tanstack/react-start-client": { - "version": "1.168.16", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-client/-/react-start-client-1.168.16.tgz", - "integrity": "sha512-1OfHgy0wpHwe2tlB3FxMeA+IMX6Il/QAMf+8UdXuimReIc2Lz3BkMLBL38k4GIxBguX9sI8EMLO5jlTZ4e1olw==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.18", - "@tanstack/router-core": "1.171.15", - "@tanstack/start-client-core": "1.170.14" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - } - }, - "node_modules/@tanstack/react-start-rsc": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-rsc/-/react-start-rsc-0.1.31.tgz", - "integrity": "sha512-WxjkXYflq550vTNJpdPyMaPC+Vyh88L5wOL+SiDTjPMGne9ad7FZmoJxqfCFEv1e7HVKMH/mMoE8619TsTNVzQ==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.18", - "@tanstack/router-core": "1.171.15", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-client-core": "1.170.14", - "@tanstack/start-fn-stubs": "1.162.0", - "@tanstack/start-plugin-core": "1.171.24", - "@tanstack/start-server-core": "1.169.17", - "@tanstack/start-storage-context": "1.167.17", - "pathe": "^2.0.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rspack/core": ">=2.0.0-0", - "@vitejs/plugin-rsc": ">=0.5.20", - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0", - "react-server-dom-rspack": ">=0.0.2" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "react-server-dom-rspack": { - "optional": true - } - } - }, - "node_modules/@tanstack/react-start-server": { - "version": "1.167.22", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-server/-/react-start-server-1.167.22.tgz", - "integrity": "sha512-eH2PeHuLfL3R5YzE9+y2FfcE4Ld1LNV2ZfrCNVPJMMJFt+9nXDaRHg9BsEmc+JkTAGzz3FKLyQEoWwpbG6Ehqg==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.18", - "@tanstack/router-core": "1.171.15", - "@tanstack/start-server-core": "1.169.17" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - } - }, "node_modules/@tanstack/react-store": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.9.3.tgz", @@ -1975,28 +1834,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@tanstack/router-cli": { - "version": "1.167.21", - "resolved": "https://registry.npmjs.org/@tanstack/router-cli/-/router-cli-1.167.21.tgz", - "integrity": "sha512-VAWzT0f1XHJx1ORNBDCjgCRHhBck0UDBYx5yldUlZgPyuDw/ip1s9pcn8h8ZE3jdTQ5e/nF4brFE/RhRca7rRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tanstack/router-generator": "1.167.21", - "chokidar": "^5.0.0", - "yargs": "^17.7.2" - }, - "bin": { - "tsr": "bin/tsr.cjs" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/@tanstack/router-core": { "version": "1.171.15", "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.171.15.tgz", @@ -2047,6 +1884,7 @@ "version": "1.167.21", "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.167.21.tgz", "integrity": "sha512-m3oXZyienj8owialdyoZ0txHQrnEx/Ra+D9kWtar5fC2cWZr5Pvxl86VY2mX5RRLC5QLKLeRGT1x4HV95wHVDQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.5", @@ -2070,6 +1908,7 @@ "version": "1.168.23", "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.168.23.tgz", "integrity": "sha512-0+PIcvnaAimFwjoEIeV3h7LKjzC8zNnp7pH2UamdKwQ9QlY99WU9V0Xl0zbM0i9hrUa/mKgWPDAzELmPUu5fMA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.28.5", @@ -2118,6 +1957,7 @@ "version": "1.162.2", "resolved": "https://registry.npmjs.org/@tanstack/router-utils/-/router-utils-1.162.2.tgz", "integrity": "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/generator": "^7.28.5", @@ -2137,123 +1977,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tanstack/start-client-core": { - "version": "1.170.14", - "resolved": "https://registry.npmjs.org/@tanstack/start-client-core/-/start-client-core-1.170.14.tgz", - "integrity": "sha512-yasBgEIFSWysL4EiFIGwp638nCoXXKiTqkc48EP2oty4OyNsZPTC1yfJ82zjq2KGkTAYtIaeMl7otqqRl1n85Q==", - "license": "MIT", - "dependencies": { - "@tanstack/router-core": "1.171.15", - "@tanstack/start-fn-stubs": "1.162.0", - "@tanstack/start-storage-context": "1.167.17", - "seroval": "^1.5.4" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-fn-stubs": { - "version": "1.162.0", - "resolved": "https://registry.npmjs.org/@tanstack/start-fn-stubs/-/start-fn-stubs-1.162.0.tgz", - "integrity": "sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==", - "license": "MIT", - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-plugin-core": { - "version": "1.171.24", - "resolved": "https://registry.npmjs.org/@tanstack/start-plugin-core/-/start-plugin-core-1.171.24.tgz", - "integrity": "sha512-l/tm+T0ntXHeIzr9kJDTJ2IDNZC0yFazjkvbEVeZsDOrJ8F+HiZmY+tXYqI5/nDYkwxY0DVQr+kGsTRVb6y2Jw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "7.27.1", - "@babel/core": "^7.28.5", - "@babel/types": "^7.28.5", - "@tanstack/router-core": "1.171.15", - "@tanstack/router-generator": "1.167.21", - "@tanstack/router-plugin": "1.168.23", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-server-core": "1.169.17", - "exsolve": "^1.0.7", - "lightningcss": "^1.32.0", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "seroval": "^1.5.4", - "source-map": "^0.7.6", - "srvx": "^0.11.9", - "tinyglobby": "^0.2.15", - "ufo": "^1.5.4", - "vitefu": "^1.1.1", - "xmlbuilder2": "^4.0.3", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rsbuild/core": "^2.0.0", - "vite": ">=7.0.0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@tanstack/start-server-core": { - "version": "1.169.17", - "resolved": "https://registry.npmjs.org/@tanstack/start-server-core/-/start-server-core-1.169.17.tgz", - "integrity": "sha512-u0N+PHJhMHnzfnlXYI9F+A/qweDe3E2X0mfkORPGIEkNQgvS548RA9fjwvixR2en5b848CfpEqUzwFhm/tQ40Q==", - "license": "MIT", - "dependencies": { - "@tanstack/history": "1.162.0", - "@tanstack/router-core": "1.171.15", - "@tanstack/start-client-core": "1.170.14", - "@tanstack/start-storage-context": "1.167.17", - "fetchdts": "^0.1.6", - "h3-v2": "npm:h3@2.0.1-rc.20", - "seroval": "^1.5.4" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-storage-context": { - "version": "1.167.17", - "resolved": "https://registry.npmjs.org/@tanstack/start-storage-context/-/start-storage-context-1.167.17.tgz", - "integrity": "sha512-ntkDyGx0PE0opIlWNAMpkMb8qkjR4uyCUOfC0CiT0STM25+EcwPuwYNfDXXeVObMrTAPgsQ4yOj3xdY0Xr4ptw==", - "license": "MIT", - "dependencies": { - "@tanstack/router-core": "1.171.15" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/@tanstack/store": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.9.3.tgz", @@ -2268,6 +1991,7 @@ "version": "1.162.0", "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.162.0.tgz", "integrity": "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==", + "dev": true, "license": "MIT", "engines": { "node": ">=20.19" @@ -2416,51 +2140,21 @@ } } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/ansis": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz", "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, "node_modules/babel-dead-code-elimination": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.23.7", @@ -2473,6 +2167,7 @@ "version": "2.11.6", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.6.tgz", "integrity": "sha512-69D/imtToCsIcAl8WBS2YaRwA4jO/j0HhU+hELqMEu9f54MoUtI6+XH5mrKU8rEFNEk/Ui1I2MK4/JkWacclGw==", + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -2485,6 +2180,7 @@ "version": "4.28.7", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2519,6 +2215,7 @@ "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2552,6 +2249,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, "license": "MIT", "dependencies": { "readdirp": "^5.0.0" @@ -2563,21 +2261,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2587,30 +2270,11 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, "node_modules/cookie-es": { @@ -2636,6 +2300,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2662,6 +2327,7 @@ "version": "8.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2671,14 +2337,8 @@ "version": "1.5.398", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz", "integrity": "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT" + "license": "ISC" }, "node_modules/enhanced-resolve": { "version": "5.24.4", @@ -2697,17 +2357,12 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/exsolve": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", - "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", - "license": "MIT" - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2725,12 +2380,6 @@ } } }, - "node_modules/fetchdts": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/fetchdts/-/fetchdts-0.1.7.tgz", - "integrity": "sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==", - "license": "MIT" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2749,21 +2398,12 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/goober": { "version": "2.1.19", "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.19.tgz", @@ -2779,31 +2419,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/h3-v2": { - "name": "h3", - "version": "2.0.1-rc.20", - "resolved": "https://registry.npmjs.org/h3/-/h3-2.0.1-rc.20.tgz", - "integrity": "sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==", - "license": "MIT", - "dependencies": { - "rou3": "^0.8.1", - "srvx": "^0.11.13" - }, - "bin": { - "h3": "bin/h3.mjs" - }, - "engines": { - "node": ">=20.11.1" - }, - "peerDependencies": { - "crossws": "^0.4.1" - }, - "peerDependenciesMeta": { - "crossws": { - "optional": true - } - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -2824,16 +2439,6 @@ "url": "https://opencollective.com/immer" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/isbot": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.2.1.tgz", @@ -2856,34 +2461,14 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -2896,6 +2481,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -3195,6 +2781,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -3222,6 +2809,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -3246,6 +2834,7 @@ "version": "2.0.51", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3293,6 +2882,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -3345,6 +2935,7 @@ "version": "3.9.6", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -3414,6 +3005,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 20.19.0" @@ -3448,16 +3040,6 @@ "redux": "^5.0.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/reselect": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", @@ -3507,12 +3089,6 @@ "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/rou3": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.8.1.tgz", - "integrity": "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==", - "license": "MIT" - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -3523,6 +3099,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3575,15 +3152,6 @@ "seroval-plugins": "~1.5.4" } }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3593,46 +3161,6 @@ "node": ">=0.10.0" } }, - "node_modules/srvx": { - "version": "0.11.22", - "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.22.tgz", - "integrity": "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==", - "license": "MIT", - "bin": { - "srvx": "bin/srvx.mjs" - }, - "engines": { - "node": ">=20.16.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -3699,12 +3227,6 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "license": "MIT" - }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -3716,6 +3238,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", @@ -3770,6 +3293,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -3883,48 +3407,12 @@ } } }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } + "license": "MIT" }, "node_modules/ws": { "version": "8.21.1", @@ -3947,70 +3435,18 @@ } } }, - "node_modules/xmlbuilder2": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz", - "integrity": "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==", - "license": "MIT", - "dependencies": { - "@oozcitak/dom": "^2.0.2", - "@oozcitak/infra": "^2.0.2", - "@oozcitak/util": "^10.0.0", - "js-yaml": "^4.1.1" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, - "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index aaa58f9..903a8c3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ }, "scripts": { "dev": "vite dev --port 3000", - "generate-routes": "tsr generate", "build": "vite build", "preview": "vite preview", "format": "biome format --write .", @@ -20,7 +19,6 @@ "@tanstack/react-devtools": "latest", "@tanstack/react-router": "latest", "@tanstack/react-router-devtools": "latest", - "@tanstack/react-start": "latest", "luxon": "^3.7.2", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -31,7 +29,7 @@ "devDependencies": { "@biomejs/biome": "2.4.5", "@tanstack/devtools-vite": "latest", - "@tanstack/router-cli": "^1.132.0", + "@tanstack/router-plugin": "^1.132.0", "@types/luxon": "^3.7.2", "@types/node": "^22.10.2", "@types/react": "^19.2.0", diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..2946a1d --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,24 @@ +import { RouterProvider } from "@tanstack/react-router"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { Provider } from "react-redux"; +import { PersistGate } from "redux-persist/integration/react"; +import { getRouter } from "#/router"; +import { persistor, store } from "#/store/store"; +import "./styles.css"; + +const router = getRouter(); + +const rootElement = document.getElementById("app"); +if (!rootElement) + throw new Error("Could not find #app element to mount the app"); + +createRoot(rootElement).render( + + + + + + + , +); diff --git a/src/pages/Welcome.tsx b/src/pages/Welcome.tsx index ccdb76b..d79f588 100644 --- a/src/pages/Welcome.tsx +++ b/src/pages/Welcome.tsx @@ -1,5 +1,11 @@ import { DateTime } from "luxon"; -import { type FC, useEffect, useState } from "react"; +import { + type EventHandler, + type FC, + type MouseEventHandler, + useEffect, + useState, +} from "react"; import { useAppDispatch, useAppSelector } from "../store/hooks"; import { clearStartTime, setStartTime } from "../store/slices/timeSlice"; import { ROUND_LENGTH_SECONDS } from "../utils/constants"; @@ -22,6 +28,14 @@ const Welcome: FC = () => { const isRunning = startTime !== null; const isOvertime = elapsedSeconds > 60; + const nextRoundClick: MouseEventHandler = (_e) => { + dispatch(clearStartTime()); + }; + + const startRoundClick: MouseEventHandler = (_e) => { + dispatch(setStartTime(DateTime.now().toISO() ?? "")); + }; + return (

By Other Means Control Timer

@@ -35,7 +49,7 @@ const Welcome: FC = () => { {isRunning ? (