diff --git a/.github/workflows/typecheck.yaml b/.github/workflows/typecheck.yaml index ba92b6bf6..ca7ceb2a9 100644 --- a/.github/workflows/typecheck.yaml +++ b/.github/workflows/typecheck.yaml @@ -9,9 +9,9 @@ jobs: typecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 - name: Install dependencies diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 39c09388c..1c5b5cdd0 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -17,9 +17,9 @@ jobs: - saflib # END WORKFLOW AREA steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 cache: "npm" diff --git a/.github/workflows/workflow-checklist.yaml b/.github/workflows/workflow-checklist.yaml index 74295f519..e2032f0dd 100644 --- a/.github/workflows/workflow-checklist.yaml +++ b/.github/workflows/workflow-checklist.yaml @@ -9,9 +9,9 @@ jobs: workflow-integration-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 diff --git a/.github/workflows/workflow-git.yaml b/.github/workflows/workflow-git.yaml index 758edfb6c..d8e0de529 100644 --- a/.github/workflows/workflow-git.yaml +++ b/.github/workflows/workflow-git.yaml @@ -9,9 +9,9 @@ jobs: workflow-git-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 diff --git a/.github/workflows/workflow-print.yaml b/.github/workflows/workflow-print.yaml index 367c8cbad..c2992b184 100644 --- a/.github/workflows/workflow-print.yaml +++ b/.github/workflows/workflow-print.yaml @@ -9,9 +9,9 @@ jobs: workflow-integration-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 diff --git a/.github/workflows/workflow-run.yaml b/.github/workflows/workflow-run.yaml index 54b1d2bfe..7e59f70d7 100644 --- a/.github/workflows/workflow-run.yaml +++ b/.github/workflows/workflow-run.yaml @@ -9,9 +9,9 @@ jobs: workflow-integration-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 diff --git a/.github/workflows/workflow-script.yaml b/.github/workflows/workflow-script.yaml index 9fa79427d..afaf4dbe0 100644 --- a/.github/workflows/workflow-script.yaml +++ b/.github/workflows/workflow-script.yaml @@ -9,9 +9,9 @@ jobs: workflow-integration-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 diff --git a/analytics-service/AnalyticsServiceBase.ts b/analytics-service/AnalyticsServiceBase.ts index b2c67578c..112deb777 100644 --- a/analytics-service/AnalyticsServiceBase.ts +++ b/analytics-service/AnalyticsServiceBase.ts @@ -1,4 +1,4 @@ -import { getSafContext } from "@saflib/node"; +import { getSafContext, getSafReporters } from "@saflib/node"; import type { SafContext } from "@saflib/node"; import type { AnalyticsService, CommonEvent, IdentifyProps } from "./types.ts"; @@ -52,6 +52,13 @@ export abstract class AnalyticsServiceBase implements AnalyticsService { event: event.event, context: mergedContext, }); + + const { log } = getSafReporters(); + log.info(`Product event: ${event.event}`, { + event: event.event, + distinct_id: distinctId, + ...(mergedContext !== undefined ? { context: mergedContext } : {}), + }); } protected abstract emitCapture(event: { diff --git a/backup/backup-sdk/Dockerfile.template b/backup/backup-sdk/Dockerfile.template index 33eac4500..d89f05f72 100644 --- a/backup/backup-sdk/Dockerfile.template +++ b/backup/backup-sdk/Dockerfile.template @@ -7,8 +7,7 @@ RUN npm install -g npm@11.14.1 WORKDIR /app #{ copy_packages }# -RUN npm install -RUN npm install @rollup/rollup-linux-arm64-gnu +RUN npm ci #{ copy_src }# diff --git a/cron/cron/Dockerfile.template b/cron/cron/Dockerfile.template index b1fa0c628..f5be43a68 100644 --- a/cron/cron/Dockerfile.template +++ b/cron/cron/Dockerfile.template @@ -4,7 +4,7 @@ FROM node:alpine3.19 WORKDIR /app #{ copy_packages }# -RUN npm install --omit=dev +RUN npm ci --omit=dev #{ copy_src }# WORKDIR /app/services/cron diff --git a/drizzle/types/file-metadata.ts b/drizzle/types/file-metadata.ts index 89fe77f02..c0dbde3f4 100644 --- a/drizzle/types/file-metadata.ts +++ b/drizzle/types/file-metadata.ts @@ -20,6 +20,7 @@ export const fileMetadataColumns = { file_original_name: text("file_original_name").notNull(), mimetype: text("mimetype").notNull(), size: integer("size").notNull(), + md5_hash: text("md5_hash"), created_at: text("created_at") .notNull() .$defaultFn(() => new Date().toISOString()), @@ -47,6 +48,7 @@ export interface FileMetadataFields { file_original_name: string; mimetype: string; size: number; + md5_hash: string | null; created_at: string; updated_at: string; } diff --git a/express/src/middleware/auth.ts b/express/src/middleware/auth.ts index 0ade4266c..4ef7bf971 100644 --- a/express/src/middleware/auth.ts +++ b/express/src/middleware/auth.ts @@ -4,6 +4,7 @@ import { AUTH_ERROR_EMAIL_VERIFICATION_REQUIRED, AUTH_ERROR_MFA_REQUIRED, } from "@saflib/sdk/auth-error-codes"; +import { typedEnv } from "@saflib/env"; interface AuthMiddlewareOptions { adminRequired?: boolean; @@ -57,7 +58,7 @@ export const makeAuthMiddleware = ( const routeRequiresMfa = Boolean(mfaRequired) || tags?.includes("mfa-required") === true || - Boolean(adminRequired); + (Boolean(adminRequired) && typedEnv.NODE_ENV === "production"); if (tags?.includes("no-auth")) { return next(); diff --git a/express/src/middleware/csrf.ts b/express/src/middleware/csrf.ts index 28b6abe1c..748087bbc 100644 --- a/express/src/middleware/csrf.ts +++ b/express/src/middleware/csrf.ts @@ -6,6 +6,8 @@ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]); /** * Enforce CSRF double-submit token validation on state-changing requests. * Skips routes tagged `no-auth` (same convention as auth middleware). + * Skips `csrf-exempt` for browser-initiated posts that cannot attach our token + * (e.g. Content-Security-Policy violation reports). */ export const makeCsrfMiddleware = (): Handler => { return (req, res, next): void => { @@ -13,7 +15,8 @@ export const makeCsrfMiddleware = (): Handler => { return next(); } - if (req.openapi?.schema?.tags?.includes("no-auth")) { + const tags = req.openapi?.schema?.tags; + if (tags?.includes("no-auth") || tags?.includes("csrf-exempt")) { return next(); } diff --git a/express/src/middleware/errors.ts b/express/src/middleware/errors.ts index 9520e240e..201a3361d 100644 --- a/express/src/middleware/errors.ts +++ b/express/src/middleware/errors.ts @@ -1,6 +1,6 @@ import createError, { HttpError } from "http-errors"; import type { Request, Response, NextFunction, Handler } from "express"; -import { safReportersStorage } from "@saflib/node"; +import { getErrorCollectors, safReportersStorage } from "@saflib/node"; import { typedEnv } from "@saflib/env"; /** * 404 Handler @@ -24,13 +24,19 @@ export const errorHandler = ( const status = err.status || 500; if (status >= 500) { - if (typedEnv.NODE_ENV === "test") { + if ( + typedEnv.NODE_ENV === "test" && + getErrorCollectors().length === 0 + ) { console.error(err.stack); } const store = safReportersStorage.getStore(); if (store) { store.logError(err); - } else { + } else if ( + typedEnv.NODE_ENV !== "test" || + getErrorCollectors().length === 0 + ) { console.log("Error", err); } } diff --git a/node/src/errors.ts b/node/src/errors.ts index 8623bf2bf..f1ca9149f 100644 --- a/node/src/errors.ts +++ b/node/src/errors.ts @@ -18,7 +18,7 @@ export const addErrorCollector = (collector: ErrorCollector) => { errorCollectors.push(collector); }; -const getErrorCollectors = () => { +export const getErrorCollectors = () => { return errorCollectors.slice(); }; diff --git a/package.json b/package.json index 99ffe2554..acd9398e5 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "@saflib/vue": "*", "@vue/tsconfig": "^0.9.1", "typescript": "~6.0.0", - "vite": "^8.0.9", "vue-router": "^5.0.0", "vue-tsc": "^3.0.6" }, diff --git a/processes/workflows/spec-project.ts b/processes/workflows/spec-project.ts index bca3bfb04..a22516d85 100644 --- a/processes/workflows/spec-project.ts +++ b/processes/workflows/spec-project.ts @@ -126,6 +126,8 @@ export const SpecProjectWorkflowDefinition = defineWorkflow< Now that you have a plan, you can write the workflows per the aligned plan to implement the spec. Only one workflow has been generated, but make as many as the plan dictates. Have the main one run the others (orchestrating them). + **Step shape:** Do not start a phase workflow (or the orchestrator) with a review-only \`PromptStepMachine\` that only tells the agent to read the spec/plan. The first step should be real work (\`CdStepMachine\`, a sub-workflow, or an implementation prompt). Put context in that first step's prompt with \`Use workflow docFiles (**.spec.md**, **.plan.md** Phase N)\` — the workflow's \`docFiles\` block already wires those paths; reading them is not a separate step. Follow \`daemon/plans/notes/2026-05-21-questionnaire-mappings/\` phase workflows as the reference shape. + For each workflow, run "npm exec saf-workflow dry-run ./path/to/workflow.ts" to make sure everything is wired up correctly. One of the more common errors is for the workflow to not include "CdStepMachine" to move into the right directory before running the workflow. Location matters. `, })), diff --git a/product/workflows/templates/.github/actions/setup-node-deps/action.yml b/product/workflows/templates/.github/actions/setup-node-deps/action.yml new file mode 100644 index 000000000..b918cdacd --- /dev/null +++ b/product/workflows/templates/.github/actions/setup-node-deps/action.yml @@ -0,0 +1,36 @@ +name: Setup Node dependencies +description: >- + Setup Node.js, restore ~/.npm and workspace node_modules caches, and run npm ci + only on cache miss. The monorepo installs into nested node_modules (e.g. + saflib/node_modules); caching only the repo root tree breaks typecheck. +inputs: + node-version: + description: Node.js version + required: false + default: "24" +runs: + using: composite + steps: + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ inputs.node-version }} + cache: npm + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: | + node_modules + saflib/**/node_modules + daemon/**/node_modules + deploy/**/node_modules + key: node-modules-v2-${{ runner.os }}-node-${{ inputs.node-version }}-${{ hashFiles('package-lock.json') }} + restore-keys: | + node-modules-v2-${{ runner.os }}-node-${{ inputs.node-version }}- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + shell: bash + run: npm ci --prefer-offline --no-audit --no-fund diff --git a/product/workflows/templates/.github/workflows/playwright.yml b/product/workflows/templates/.github/workflows/playwright.yml index 6419aa5fd..02fd9aa80 100644 --- a/product/workflows/templates/.github/workflows/playwright.yml +++ b/product/workflows/templates/.github/workflows/playwright.yml @@ -10,15 +10,10 @@ jobs: run: working-directory: . steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: lts/* - cache: "npm" - - name: Install submodules - run: git submodule update --init - - name: Install dependencies - run: npm ci + submodules: recursive + - uses: ./.github/actions/setup-node-deps # For some reason, github actions hang when I do `npm exec saf-docker generate`. So we run the script directly. - name: Generate Docker run: node --experimental-strip-types --disable-warning=ExperimentalWarning saflib/dev-tools/src/saf-docker-cli.ts generate @@ -36,14 +31,14 @@ jobs: - name: Shut down run: docker stop $(docker ps -a -q) # TODO: gather all logs and upload them as artifacts - # - uses: actions/upload-artifact@v4 + # - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 # if: ${{ !cancelled() }} # with: # name: playwright-report # path: ./clients/web-auth/playwright-report/ # retention-days: 30 - name: Upload docker compose logs - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: ${{ !cancelled() }} with: name: last-docker-compose-log diff --git a/product/workflows/templates/.github/workflows/push.yml b/product/workflows/templates/.github/workflows/push.yml index eac95a88b..81a9ba138 100644 --- a/product/workflows/templates/.github/workflows/push.yml +++ b/product/workflows/templates/.github/workflows/push.yml @@ -26,21 +26,13 @@ jobs: env: CONTAINER_REGISTRY: ghcr.io/${{ github.repository_owner }} steps: - - uses: actions/checkout@v6 - - name: Install submodules - run: git submodule update --init - - - name: Set up Node.js - uses: actions/setup-node@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: 24 - cache: npm - - - name: Install dependencies - run: npm ci + submodules: recursive + - uses: ./.github/actions/setup-node-deps - name: Log in to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/product/workflows/templates/.github/workflows/typecheck.yml b/product/workflows/templates/.github/workflows/typecheck.yml index a6be1fe3d..a6ffed1ce 100644 --- a/product/workflows/templates/.github/workflows/typecheck.yml +++ b/product/workflows/templates/.github/workflows/typecheck.yml @@ -9,15 +9,9 @@ jobs: typecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - name: Setup Node.js - uses: actions/setup-node@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: 24 - cache: "npm" - - name: Install submodules - run: git submodule update --init - - name: Install dependencies - run: npm ci + submodules: recursive + - uses: ./.github/actions/setup-node-deps - name: Run Typecheck run: npm run typecheck diff --git a/product/workflows/templates/.github/workflows/unit-tests.yaml b/product/workflows/templates/.github/workflows/unit-tests.yaml index b00709f5e..106a52c2e 100644 --- a/product/workflows/templates/.github/workflows/unit-tests.yaml +++ b/product/workflows/templates/.github/workflows/unit-tests.yaml @@ -17,20 +17,10 @@ jobs: - __product-name__ # END WORKFLOW AREA steps: - - uses: actions/checkout@v6 - - name: Setup Node.js - uses: actions/setup-node@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: 24 - cache: "npm" - - name: Install submodules - run: git submodule update --init - - name: Install dependencies - run: npm ci - - # Tests fail to run if this one package isn't installed - - name: Install rollup for linux - run: npm install @rollup/rollup-linux-x64-gnu + submodules: recursive + - uses: ./.github/actions/setup-node-deps - name: Run Unit Tests run: npm run test -- --project='*${{ matrix.project }}*' diff --git a/product/workflows/templates/__product-name__/dev/Dockerfile.template b/product/workflows/templates/__product-name__/dev/Dockerfile.template index df5d13875..836f8e560 100644 --- a/product/workflows/templates/__product-name__/dev/Dockerfile.template +++ b/product/workflows/templates/__product-name__/dev/Dockerfile.template @@ -14,7 +14,7 @@ RUN npm install -g npm@11.14.1 WORKDIR /app #{ copy_packages }# -RUN npm install --omit=dev +RUN npm ci --omit=dev #{ copy_src }# diff --git a/product/workflows/templates/__product-name__/dev/kratos/kratos.yml b/product/workflows/templates/__product-name__/dev/kratos/kratos.yml index 8b29f9800..38bae434a 100644 --- a/product/workflows/templates/__product-name__/dev/kratos/kratos.yml +++ b/product/workflows/templates/__product-name__/dev/kratos/kratos.yml @@ -13,11 +13,13 @@ serve: selfservice: default_browser_return_url: http://auth.docker.localhost/ - # Permit return_to from any app on this dev domain (e.g. app.recipes.docker.localhost). - # Root host is listed separately; *.docker.localhost does not match the apex. + # Only apex and product hosts (no subdomain wildcard — T-9 open redirect). allowed_return_urls: - http://docker.localhost/ - - http://*.docker.localhost/ + - http://app.docker.localhost/ + - http://admin.docker.localhost/ + - http://auth.docker.localhost/ + - http://account.docker.localhost/ flows: registration: diff --git a/product/workflows/templates/__product-name__/service/monolith/Dockerfile.template b/product/workflows/templates/__product-name__/service/monolith/Dockerfile.template index ba3b74daa..414a69f46 100644 --- a/product/workflows/templates/__product-name__/service/monolith/Dockerfile.template +++ b/product/workflows/templates/__product-name__/service/monolith/Dockerfile.template @@ -4,7 +4,7 @@ FROM node:alpine3.19 WORKDIR /app #{ copy_packages }# -RUN npm install --omit=dev +RUN npm ci --omit=dev #{ copy_src }# WORKDIR /app/__product-name__/service/monolith diff --git a/product/workflows/templates/deploy/Dockerfile.prod b/product/workflows/templates/deploy/Dockerfile.prod index b13482332..e2b35e215 100644 --- a/product/workflows/templates/deploy/Dockerfile.prod +++ b/product/workflows/templates/deploy/Dockerfile.prod @@ -11,7 +11,7 @@ RUN export $(grep -v "^#" /app/env.__product-name__.prod-local | xargs) && DOMAI # END WORKFLOW AREA FROM caddy:2.11.3-builder-alpine AS caddy-rebuild -RUN xcaddy build v2.11.2 +RUN xcaddy build v2.11.3 FROM caddy:2.11.3-alpine COPY --from=caddy-rebuild /usr/bin/caddy /usr/bin/caddy diff --git a/product/workflows/templates/deploy/kratos-prod-local/kratos.yml b/product/workflows/templates/deploy/kratos-prod-local/kratos.yml index cd36ec6c7..e2ad6168a 100644 --- a/product/workflows/templates/deploy/kratos-prod-local/kratos.yml +++ b/product/workflows/templates/deploy/kratos-prod-local/kratos.yml @@ -13,10 +13,13 @@ serve: selfservice: default_browser_return_url: http://auth.docker.localhost/ - # Same as recipes/dev: any HTTP origin on docker.localhost for local prod-like stacks. + # Only apex and product hosts (no subdomain wildcard — T-9 open redirect). allowed_return_urls: - http://docker.localhost/ - - http://*.docker.localhost/ + - http://app.docker.localhost/ + - http://admin.docker.localhost/ + - http://auth.docker.localhost/ + - http://account.docker.localhost/ flows: registration: diff --git a/product/workflows/templates/deploy/remote-assets/kratos/kratos.yml b/product/workflows/templates/deploy/remote-assets/kratos/kratos.yml index 1656875f4..e13bc5d2f 100644 --- a/product/workflows/templates/deploy/remote-assets/kratos/kratos.yml +++ b/product/workflows/templates/deploy/remote-assets/kratos/kratos.yml @@ -13,10 +13,13 @@ serve: selfservice: default_browser_return_url: https://auth.__domain-name__/ - # Permit return_to from any HTTPS app on this domain (wildcard does not match apex). + # Only apex and product hosts (no subdomain wildcard — T-9 open redirect). allowed_return_urls: - https://__domain-name__/ - - https://*.__domain-name__/ + - https://app.__domain-name__/ + - https://admin.__domain-name__/ + - https://auth.__domain-name__/ + - https://account.__domain-name__/ flows: registration: diff --git a/sdk/src/client.ts b/sdk/src/client.ts index 8a59d9771..456feaaf7 100644 --- a/sdk/src/client.ts +++ b/sdk/src/client.ts @@ -1,6 +1,5 @@ import { QueryClient } from "@tanstack/vue-query"; import createClient from "openapi-fetch"; -import { isTestEnv } from "@saflib/vue"; import { getProtocol, getHost } from "@saflib/links"; import { TanstackError } from "./errors.ts"; import type { ClientResult } from "./types.ts"; @@ -94,9 +93,6 @@ export const handleClientMethod = async ( // This is because UI should not render the untranslated error message, but instead // give the user a message based on the HTTP status or, if that's not sufficient, // the error code. - if (isTestEnv()) { - console.error(result.error); - } throw new TanstackError(result.response.status, result.error.code); } if (result.data === undefined) { diff --git a/sdk/workflows/templates/Dockerfile.template b/sdk/workflows/templates/Dockerfile.template index 7b2543c36..9d2b959ee 100644 --- a/sdk/workflows/templates/Dockerfile.template +++ b/sdk/workflows/templates/Dockerfile.template @@ -7,8 +7,7 @@ RUN npm install -g npm@11.14.1 WORKDIR /app #{ copy_packages }# -RUN npm install -RUN npm install @rollup/rollup-linux-arm64-gnu +RUN npm ci #{ copy_src }# diff --git a/sentry/package.json b/sentry/package.json index 152cc3573..6c7dafe69 100644 --- a/sentry/package.json +++ b/sentry/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@saflib/vitest": "*", "@saflib/workflows": "*", - "vite": "^8.0.7", + "vite": "8.0.13", "vitest": "^3.2.4" } } diff --git a/service/workflows/service-templates/Dockerfile.template b/service/workflows/service-templates/Dockerfile.template index 562df1397..044181f6d 100644 --- a/service/workflows/service-templates/Dockerfile.template +++ b/service/workflows/service-templates/Dockerfile.template @@ -4,7 +4,7 @@ FROM node:alpine3.19 WORKDIR /app #{ copy_packages }# -RUN npm install --omit=dev +RUN npm ci --omit=dev #{ copy_src }# WORKDIR /app/__service-group-dir__/__service-name__-service diff --git a/vite/package.json b/vite/package.json index fe91e0056..5d378197f 100644 --- a/vite/package.json +++ b/vite/package.json @@ -16,7 +16,7 @@ "@saflib/vue": "*", "@vitejs/plugin-vue": "^6.0.1", "rollup-plugin-ignore": "^1.0.10", - "vite": "^8.0.0", + "vite": "8.0.13", "vite-plugin-vue-devtools": "^8.0.1", "vite-plugin-vuetify": "^2.1.3" }, diff --git a/vitest/base-vitest.config.js b/vitest/base-vitest.config.js index 933924714..1c81dde75 100644 --- a/vitest/base-vitest.config.js +++ b/vitest/base-vitest.config.js @@ -1,7 +1,11 @@ +import path from "node:path"; import { defineConfig } from "vitest/config"; +const testSetupFile = path.join(import.meta.dirname, "test-setup.ts"); + export const defaultConfig = defineConfig({ test: { + setupFiles: [testSetupFile], /* * By default, isolate tests. There's no apparent change in performance, but sometimes tests * will break in CI or when you run them locally with --no-file-parallelism, particularly when diff --git a/vitest/test-setup.ts b/vitest/test-setup.ts new file mode 100644 index 000000000..94c9de251 --- /dev/null +++ b/vitest/test-setup.ts @@ -0,0 +1,8 @@ +import { addErrorCollector } from "@saflib/node"; + +/** + * Register a no-op collector so {@link defaultErrorReporter} and + * {@link queryWrapper} do not print expected error stacks to stderr during tests. + * Suites that assert on logging can register their own collector afterward. + */ +addErrorCollector(() => {}); diff --git a/vue/components/UsPhoneNumberInput.vue b/vue/components/UsPhoneNumberInput.vue index f8013dab7..e4bbfffaf 100644 --- a/vue/components/UsPhoneNumberInput.vue +++ b/vue/components/UsPhoneNumberInput.vue @@ -1,5 +1,6 @@