diff --git a/.github/workflows/local-ci.yml b/.github/workflows/local-ci.yml index 109c9578..59d5c66d 100644 --- a/.github/workflows/local-ci.yml +++ b/.github/workflows/local-ci.yml @@ -87,6 +87,10 @@ jobs: max_attempts: 3 command: npm i -g firebase-tools@14 + # ✅ Ensure Data Connect SDKs are generated before emulator tests + - name: Generate Data Connect SDKs + run: firebase dataconnect:sdk:generate + # Build packages before testing - name: Build packages run: pnpm turbo build @@ -94,7 +98,6 @@ jobs: # Verify build outputs - name: Verify build outputs run: | - # Check all packages for dist directories MISSING_BUILDS="" for PKG_DIR in packages/*; do if [ -d "$PKG_DIR" ] && [ -f "$PKG_DIR/package.json" ]; then diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 373a1485..4d8a7117 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,6 +109,10 @@ jobs: max_attempts: 3 command: npm i -g firebase-tools@latest + # ✅ Ensure Data Connect SDKs are generated before emulator tests + - name: Generate Data Connect SDKs + run: firebase dataconnect:sdk:generate + - name: Run tests with emulator run: pnpm test:emulator @@ -118,7 +122,6 @@ jobs: - name: Verify build outputs run: | echo "Checking build outputs..." - # Check all packages for dist directories MISSING_BUILDS="" for PKG_DIR in packages/*; do if [ -d "$PKG_DIR" ] && [ -f "$PKG_DIR/package.json" ]; then @@ -128,27 +131,22 @@ jobs: fi fi done - if [ -n "$MISSING_BUILDS" ]; then echo "❌ Build outputs missing for: $MISSING_BUILDS" exit 1 fi - echo "✅ All build outputs verified" - name: Validate changesets run: | set -e CHANGESET_FILES=$(find .changeset -name "*.md" -type f ! -name "README.md" 2>/dev/null || true) - if [ -z "$CHANGESET_FILES" ]; then echo "❌ No changesets found!" echo "" echo "Please create changesets locally with: pnpm changeset" - echo "Changesets should be created during development, not during release." exit 1 fi - CHANGESET_COUNT=$(echo "$CHANGESET_FILES" | wc -l | tr -d ' ') echo "✅ Found $CHANGESET_COUNT changeset(s):" echo "$CHANGESET_FILES" | while read -r file; do @@ -182,22 +180,19 @@ jobs: done echo "📦 Version changes that would be applied:" - # Configure git user for changeset version command git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - # Save current HEAD reference before making changes + ORIGINAL_HEAD=$(git rev-parse HEAD) - # Create a temporary branch for dry run with unique name FALLBACK_ID=${GITHUB_RUN_ID:-$RANDOM$RANDOM} TEMP_BRANCH="dry-run-temp-$FALLBACK_ID" git checkout -b "$TEMP_BRANCH" pnpm changeset version - + echo "" echo "🔍 Changed files:" git diff --name-status "$ORIGINAL_HEAD" - + echo "" echo "🔍 Package version changes:" VERSION_CHANGES=$(git diff "$ORIGINAL_HEAD" -- '**/package.json' | grep -E "^[+-]\s*\"version\"" || true) @@ -206,10 +201,9 @@ jobs: else echo "$VERSION_CHANGES" fi - - # Clean up + git checkout - git branch -D "$TEMP_BRANCH" - + echo "" echo "✅ Dry run completed successfully" diff --git a/.github/workflows/repro.yml b/.github/workflows/repro.yml new file mode 100644 index 00000000..268ce2de --- /dev/null +++ b/.github/workflows/repro.yml @@ -0,0 +1,48 @@ +name: Repro + +on: + workflow_dispatch: + +jobs: + repro: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Install Firebase CLI + run: npm i -g firebase-tools@latest + + - name: Create minimal firebase.json (dataconnect only) + run: | + cat > firebase.json <<'JSON' + { + "emulators": { + "dataconnect": { "port": 9155 } + } + } + JSON + cat firebase.json + + - name: Repro — start Data Connect emulator + run: | + set -euxo pipefail + firebase --version + firebase emulators:exec \ + --project demo-test-project \ + --only dataconnect \ + --debug \ + "node -e \"setTimeout(()=>{}, 15000)\"" diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 37147853..c8602088 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -143,6 +143,10 @@ jobs: fi echo "✅ All build outputs verified" + # ✅ Generate Data Connect SDK before running emulators + - name: Generate Data Connect SDKs + run: firebase dataconnect:sdk:generate + # Run tests with all emulators (auth, firestore, and data-connect) - name: Run tests with emulator run: pnpm test:emulator diff --git a/dataconnect-sdk/js/default-connector/esm/index.esm.js b/dataconnect-sdk/js/default-connector/esm/index.esm.js index 09642c81..2ff02515 100644 --- a/dataconnect-sdk/js/default-connector/esm/index.esm.js +++ b/dataconnect-sdk/js/default-connector/esm/index.esm.js @@ -6,39 +6,6 @@ export const connectorConfig = { location: 'us-central1' }; -export const listMoviesRef = (dc) => { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'ListMovies'); -} -listMoviesRef.operationName = 'ListMovies'; - -export function listMovies(dc) { - return executeQuery(listMoviesRef(dc)); -} - -export const getMovieByIdRef = (dcOrVars, vars) => { - const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetMovieById', inputVars); -} -getMovieByIdRef.operationName = 'GetMovieById'; - -export function getMovieById(dcOrVars, vars) { - return executeQuery(getMovieByIdRef(dcOrVars, vars)); -} - -export const getMetaRef = (dc) => { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetMeta'); -} -getMetaRef.operationName = 'GetMeta'; - -export function getMeta(dc) { - return executeQuery(getMetaRef(dc)); -} - export const createMovieRef = (dcOrVars, vars) => { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); @@ -94,3 +61,36 @@ export function deleteMeta(dcOrVars, vars) { return executeMutation(deleteMetaRef(dcOrVars, vars)); } +export const listMoviesRef = (dc) => { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'ListMovies'); +} +listMoviesRef.operationName = 'ListMovies'; + +export function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +} + +export const getMovieByIdRef = (dcOrVars, vars) => { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +getMovieByIdRef.operationName = 'GetMovieById'; + +export function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +} + +export const getMetaRef = (dc) => { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'GetMeta'); +} +getMetaRef.operationName = 'GetMeta'; + +export function getMeta(dc) { + return executeQuery(getMetaRef(dc)); +} + diff --git a/dataconnect-sdk/js/default-connector/index.cjs.js b/dataconnect-sdk/js/default-connector/index.cjs.js index e92212c1..1de60aa6 100644 --- a/dataconnect-sdk/js/default-connector/index.cjs.js +++ b/dataconnect-sdk/js/default-connector/index.cjs.js @@ -7,42 +7,6 @@ const connectorConfig = { }; exports.connectorConfig = connectorConfig; -const listMoviesRef = (dc) => { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'ListMovies'); -} -listMoviesRef.operationName = 'ListMovies'; -exports.listMoviesRef = listMoviesRef; - -exports.listMovies = function listMovies(dc) { - return executeQuery(listMoviesRef(dc)); -}; - -const getMovieByIdRef = (dcOrVars, vars) => { - const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetMovieById', inputVars); -} -getMovieByIdRef.operationName = 'GetMovieById'; -exports.getMovieByIdRef = getMovieByIdRef; - -exports.getMovieById = function getMovieById(dcOrVars, vars) { - return executeQuery(getMovieByIdRef(dcOrVars, vars)); -}; - -const getMetaRef = (dc) => { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetMeta'); -} -getMetaRef.operationName = 'GetMeta'; -exports.getMetaRef = getMetaRef; - -exports.getMeta = function getMeta(dc) { - return executeQuery(getMetaRef(dc)); -}; - const createMovieRef = (dcOrVars, vars) => { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); @@ -102,3 +66,39 @@ exports.deleteMetaRef = deleteMetaRef; exports.deleteMeta = function deleteMeta(dcOrVars, vars) { return executeMutation(deleteMetaRef(dcOrVars, vars)); }; + +const listMoviesRef = (dc) => { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'ListMovies'); +} +listMoviesRef.operationName = 'ListMovies'; +exports.listMoviesRef = listMoviesRef; + +exports.listMovies = function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +}; + +const getMovieByIdRef = (dcOrVars, vars) => { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +getMovieByIdRef.operationName = 'GetMovieById'; +exports.getMovieByIdRef = getMovieByIdRef; + +exports.getMovieById = function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +}; + +const getMetaRef = (dc) => { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'GetMeta'); +} +getMetaRef.operationName = 'GetMeta'; +exports.getMetaRef = getMetaRef; + +exports.getMeta = function getMeta(dc) { + return executeQuery(getMetaRef(dc)); +}; diff --git a/dataconnect-sdk/js/default-connector/index.d.ts b/dataconnect-sdk/js/default-connector/index.d.ts index 2ea08ad7..be76eb72 100644 --- a/dataconnect-sdk/js/default-connector/index.d.ts +++ b/dataconnect-sdk/js/default-connector/index.d.ts @@ -93,42 +93,6 @@ export interface UpsertMovieVariables { imageUrl: string; } -interface ListMoviesRef { - /* Allow users to create refs without passing in DataConnect */ - (): QueryRef; - /* Allow users to pass in custom DataConnect instances */ - (dc: DataConnect): QueryRef; - operationName: string; -} -export const listMoviesRef: ListMoviesRef; - -export function listMovies(): QueryPromise; -export function listMovies(dc: DataConnect): QueryPromise; - -interface GetMovieByIdRef { - /* Allow users to create refs without passing in DataConnect */ - (vars: GetMovieByIdVariables): QueryRef; - /* Allow users to pass in custom DataConnect instances */ - (dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; - operationName: string; -} -export const getMovieByIdRef: GetMovieByIdRef; - -export function getMovieById(vars: GetMovieByIdVariables): QueryPromise; -export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; - -interface GetMetaRef { - /* Allow users to create refs without passing in DataConnect */ - (): QueryRef; - /* Allow users to pass in custom DataConnect instances */ - (dc: DataConnect): QueryRef; - operationName: string; -} -export const getMetaRef: GetMetaRef; - -export function getMeta(): QueryPromise; -export function getMeta(dc: DataConnect): QueryPromise; - interface CreateMovieRef { /* Allow users to create refs without passing in DataConnect */ (vars: CreateMovieVariables): MutationRef; @@ -189,3 +153,39 @@ export const deleteMetaRef: DeleteMetaRef; export function deleteMeta(vars: DeleteMetaVariables): MutationPromise; export function deleteMeta(dc: DataConnect, vars: DeleteMetaVariables): MutationPromise; +interface ListMoviesRef { + /* Allow users to create refs without passing in DataConnect */ + (): QueryRef; + /* Allow users to pass in custom DataConnect instances */ + (dc: DataConnect): QueryRef; + operationName: string; +} +export const listMoviesRef: ListMoviesRef; + +export function listMovies(): QueryPromise; +export function listMovies(dc: DataConnect): QueryPromise; + +interface GetMovieByIdRef { + /* Allow users to create refs without passing in DataConnect */ + (vars: GetMovieByIdVariables): QueryRef; + /* Allow users to pass in custom DataConnect instances */ + (dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; + operationName: string; +} +export const getMovieByIdRef: GetMovieByIdRef; + +export function getMovieById(vars: GetMovieByIdVariables): QueryPromise; +export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; + +interface GetMetaRef { + /* Allow users to create refs without passing in DataConnect */ + (): QueryRef; + /* Allow users to pass in custom DataConnect instances */ + (dc: DataConnect): QueryRef; + operationName: string; +} +export const getMetaRef: GetMetaRef; + +export function getMeta(): QueryPromise; +export function getMeta(dc: DataConnect): QueryPromise; + diff --git a/examples/react/useGetIdTokenQuery/package.json b/examples/react/useGetIdTokenQuery/package.json index 39d53f05..3642adb1 100644 --- a/examples/react/useGetIdTokenQuery/package.json +++ b/examples/react/useGetIdTokenQuery/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "dev:emulator": "cd ../../../ && firebase emulators:exec --project test-project 'cd examples/react/useGetIdTokenQuery && vite'", + "dev:emulator": "cd ../../../ && firebase emulators:exec --project demo-test-project 'cd examples/react/useGetIdTokenQuery && vite'", "build": "npx vite build", "preview": "vite preview" }, diff --git a/firebase.json b/firebase.json index c6f7bf22..0a429895 100644 --- a/firebase.json +++ b/firebase.json @@ -13,7 +13,8 @@ "port": 9000 }, "dataconnect": { - "port": 9399 + "port": 9399, + "host": "127.0.0.1" }, "ui": { "enabled": true diff --git a/package.json b/package.json index 8a42e38a..b1727b07 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "packageManager": "pnpm@10.10.0", "scripts": { "test": "turbo test", - "test:emulator": "firebase emulators:exec --project test-project \"pnpm turbo test:ci\"", + "test:emulator": "firebase emulators:exec --project demo-test-project \"pnpm turbo test:ci\"", "serve:coverage": "npx serve coverage", - "emulator": "firebase emulators:start --project test-project", + "emulator": "firebase emulators:start --project demo-test-project", "emulator:kill": "lsof -t -i:4001 -i:8080 -i:9000 -i:9099 -i:9199 -i:8085 -i:9399 -i:9299 | xargs kill -9", "format": "biome check .", "format:fix": "biome check . --write", diff --git a/packages/react/package.json b/packages/react/package.json index 4ff88fb8..9ecacdd2 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -8,7 +8,7 @@ "test:ci": "vitest --dom --coverage --run", "build": "tsup", "serve:coverage": "npx serve coverage", - "emulator": "firebase emulators:start --project test-project", + "emulator": "firebase emulators:start --project demo-test-project", "emulator:kill": "lsof -t -i:4001 -i:8080 -i:9000 -i:9099 -i:9199 -i:8085 | xargs kill -9", "check": "tsc --noEmit", "publish-package": "pnpm run build && cd dist && npm publish" diff --git a/packages/react/vitest/utils.ts b/packages/react/vitest/utils.ts index cdf1d667..25c275a8 100644 --- a/packages/react/vitest/utils.ts +++ b/packages/react/vitest/utils.ts @@ -13,7 +13,7 @@ import { expect } from "vitest"; import { connectorConfig } from "@/dataconnect/default-connector"; const firebaseTestingOptions = { - projectId: "test-project", + projectId: "demo-test-project", apiKey: "test-api-key", authDomain: "test-auth-domain", }; @@ -38,7 +38,7 @@ if (!firebaseApp) { async function wipeFirestore() { const response = await fetch( - "http://localhost:8080/emulator/v1/projects/test-project/databases/(default)/documents", + "http://localhost:8080/emulator/v1/projects/demo-test-project/databases/(default)/documents", { method: "DELETE", }, @@ -51,7 +51,7 @@ async function wipeFirestore() { async function wipeAuth() { const response = await fetch( - "http://localhost:9099/emulator/v1/projects/test-project/accounts", + "http://localhost:9099/emulator/v1/projects/demo-test-project/accounts", { method: "DELETE", },