diff --git a/.firebaserc b/.firebaserc deleted file mode 100644 index 40c5a6ed..00000000 --- a/.firebaserc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "projects": { - "default": "demo-project" - }, - "targets": {}, - "etags": {} -} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..d4b60469 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "name": "vscode-jest-tests.v2", + "request": "launch", + "runtimeExecutable": "yarn", + "args": [ + "test", + "--watch-all=false", + "--test-name-pattern", + "${jest.testNamePattern}", + "--test-path-pattern", + "${jest.testFilePattern}" + ], + "cwd": "${workspaceFolder}/packages/angular", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true + } + + ] +} \ No newline at end of file diff --git a/dataconnect-sdk/js/default-connector/README.md b/dataconnect-sdk/js/default-connector/README.md new file mode 100644 index 00000000..e786f630 --- /dev/null +++ b/dataconnect-sdk/js/default-connector/README.md @@ -0,0 +1,531 @@ +# Generated TypeScript README +This README will guide you through the process of using the generated TypeScript SDK package for the connector `default`. It will also provide examples on how to use your generated SDK to call your Data Connect queries and mutations. + +***NOTE:** This README is generated alongside the generated SDK. If you make changes to this file, they will be overwritten when the SDK is regenerated.* + +You can use this generated SDK by importing from the package `@dataconnect/default-connector` as shown below. Both CommonJS and ESM imports are supported. +You can also follow the instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#set-client). + +# Accessing the connector +A connector is a collection of queries and mutations. One SDK is generated for each connector - this SDK is generated for the connector `default`. + +You can find more information about connectors in the [Data Connect documentation](https://firebase.google.com/docs/data-connect#how-does). + +In order to call Data Connect queries and mutations, you need to create an instance of the connector in your application code. + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@dataconnect/default-connector'; + +const connector: DataConnect = getDataConnect(connectorConfig); +``` + +## Connecting to the local Emulator +By default, the connector will connect to the production service. + +To connect to the emulator, you can use the following code. +You can also follow the emulator instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#instrument-clients). + +```javascript +// add connectDataConnectEmulator to your imports +import { connectDataConnectEmulator, getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@dataconnect/default-connector'; + +const connector: DataConnect = getDataConnect(connectorConfig); +connectDataConnectEmulator(connector, 'localhost', 9399); +``` + +After it's initialized, you can call your Data Connect [queries](#queries) and [mutations](#mutations) from your generated SDK. + +# Queries +There are two ways to execute a Data Connect Query using the generated Web SDK: +- Using a Query Reference function, which returns a `QueryRef` + - The `QueryRef` can be used as an argument to `executeQuery()`, which will execute the Query and return a `QueryPromise` +- Using an action shortcut function, which returns a `QueryPromise` + - Calling the action shortcut function will execute the Query and return a `QueryPromise` + +The following is true for both the action shortcut function and the `QueryRef` function: +- The `QueryPromise` returned will resolve to the result of the Query once it has finished executing +- If the Query accepts arguments, both the action shortcut function and the `QueryRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Query +- Both functions can be called with or without passing in a `DataConnect` instance as an argument + +Below are examples of how to use the `default` connector's generated functions to execute each query. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-queries). + +## ListMovies +You can execute the `ListMovies` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +listMovies(): QueryPromise; + +listMoviesRef(): (QueryRef & { __angular?: false }); +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. +```javascript +listMovies(dc: DataConnect): QueryPromise; + +listMoviesRef(dc: DataConnect): (QueryRef & { __angular?: false }); +``` + +### Variables +The `ListMovies` query has no variables. +### Return Type +Recall that executing the `ListMovies` query returns a `QueryPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `ListMoviesData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface ListMoviesData { + movies: ({ + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key)[]; +} +``` +### Using `ListMovies`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, listMovies } from '@dataconnect/default-connector'; + +// Call the `listMovies()` function to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await listMovies(); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await listMovies(connector); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +listMovies().then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +### Using `ListMovies`'s `QueryRef` function + +```javascript +import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { connectorConfig, listMoviesRef } from '@dataconnect/default-connector'; + +// Call the `listMoviesRef()` function to get a reference to the query. +const ref = listMoviesRef(); + +// You can also pass in a `DataConnect` instance to the `QueryRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = listMoviesRef(connector); + +// Call `executeQuery()` on the reference to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeQuery(ref); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +executeQuery(ref).then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +## GetMovieById +You can execute the `GetMovieById` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +getMovieById(vars: GetMovieByIdVariables): QueryPromise; + +getMovieByIdRef(vars: GetMovieByIdVariables): (QueryRef & { __angular?: false }); +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. +```javascript +getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; + +getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): (QueryRef & { __angular?: false }); +``` + +### Variables +The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface GetMovieByIdVariables { + id: UUIDString; +} +``` +### Return Type +Recall that executing the `GetMovieById` query returns a `QueryPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `GetMovieByIdData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface GetMovieByIdData { + movie?: { + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key; +} +``` +### Using `GetMovieById`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, getMovieById, GetMovieByIdVariables } from '@dataconnect/default-connector'; +// The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: +const getMovieByIdVars: GetMovieByIdVariables = { + id: ..., +} + +// Call the `getMovieById()` function to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await getMovieById(getMovieByIdVars); +// Variables can be defined inline as well. +const { data } = await getMovieById({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await getMovieById(connector, getMovieByIdVars); + +console.log(data.movie); + +// Or, you can use the `Promise` API. +getMovieById(getMovieByIdVars).then((response) => { + const data = response.data; + console.log(data.movie); +}); +``` + +### Using `GetMovieById`'s `QueryRef` function + +```javascript +import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { connectorConfig, getMovieByIdRef, GetMovieByIdVariables } from '@dataconnect/default-connector'; +// The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: +const getMovieByIdVars: GetMovieByIdVariables = { + id: ..., +} + +// Call the `getMovieByIdRef()` function to get a reference to the query. +const ref = getMovieByIdRef(getMovieByIdVars); +// Variables can be defined inline as well. +const ref = getMovieByIdRef({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the `QueryRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = getMovieByIdRef(connector, getMovieByIdVars); + +// Call `executeQuery()` on the reference to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeQuery(ref); + +console.log(data.movie); + +// Or, you can use the `Promise` API. +executeQuery(ref).then((response) => { + const data = response.data; + console.log(data.movie); +}); +``` + +# Mutations +There are two ways to execute a Data Connect Mutation using the generated Web SDK: +- Using a Mutation Reference function, which returns a `MutationRef` + - The `MutationRef` can be used as an argument to `executeMutation()`, which will execute the Mutation and return a `MutationPromise` +- Using an action shortcut function, which returns a `MutationPromise` + - Calling the action shortcut function will execute the Mutation and return a `MutationPromise` + +The following is true for both the action shortcut function and the `MutationRef` function: +- The `MutationPromise` returned will resolve to the result of the Mutation once it has finished executing +- If the Mutation accepts arguments, both the action shortcut function and the `MutationRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Mutation +- Both functions can be called with or without passing in a `DataConnect` instance as an argument + +Below are examples of how to use the `default` connector's generated functions to execute each mutation. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-mutations). + +## CreateMovie +You can execute the `CreateMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +createMovie(vars: CreateMovieVariables): MutationPromise; + +createMovieRef(vars: CreateMovieVariables): (MutationRef & { __angular?: false }); +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; + +createMovieRef(dc: DataConnect, vars: CreateMovieVariables): (MutationRef & { __angular?: false }); +``` + +### Variables +The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface CreateMovieVariables { + title: string; + genre: string; + imageUrl: string; +} +``` +### Return Type +Recall that executing the `CreateMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `CreateMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface CreateMovieData { + movie_insert: Movie_Key; +} +``` +### Using `CreateMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, createMovie, CreateMovieVariables } from '@dataconnect/default-connector'; +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +} + +// Call the `createMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await createMovie(createMovieVars); +// Variables can be defined inline as well. +const { data } = await createMovie({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await createMovie(connector, createMovieVars); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +createMovie(createMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + +### Using `CreateMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, createMovieRef, CreateMovieVariables } from '@dataconnect/default-connector'; +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +} + +// Call the `createMovieRef()` function to get a reference to the mutation. +const ref = createMovieRef(createMovieVars); +// Variables can be defined inline as well. +const ref = createMovieRef({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = createMovieRef(connector, createMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + +## UpsertMovie +You can execute the `UpsertMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +upsertMovie(vars: UpsertMovieVariables): MutationPromise; + +upsertMovieRef(vars: UpsertMovieVariables): (MutationRef & { __angular?: false }); +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; + +upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): (MutationRef & { __angular?: false }); +``` + +### Variables +The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface UpsertMovieVariables { + id: UUIDString; + title: string; + imageUrl: string; +} +``` +### Return Type +Recall that executing the `UpsertMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `UpsertMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface UpsertMovieData { + movie_upsert: Movie_Key; +} +``` +### Using `UpsertMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, upsertMovie, UpsertMovieVariables } from '@dataconnect/default-connector'; +// The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: +const upsertMovieVars: UpsertMovieVariables = { + id: ..., + title: ..., + imageUrl: ..., +} + +// Call the `upsertMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await upsertMovie(upsertMovieVars); +// Variables can be defined inline as well. +const { data } = await upsertMovie({ id: ..., title: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await upsertMovie(connector, upsertMovieVars); + +console.log(data.movie_upsert); + +// Or, you can use the `Promise` API. +upsertMovie(upsertMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_upsert); +}); +``` + +### Using `UpsertMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, upsertMovieRef, UpsertMovieVariables } from '@dataconnect/default-connector'; +// The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: +const upsertMovieVars: UpsertMovieVariables = { + id: ..., + title: ..., + imageUrl: ..., +} + +// Call the `upsertMovieRef()` function to get a reference to the mutation. +const ref = upsertMovieRef(upsertMovieVars); +// Variables can be defined inline as well. +const ref = upsertMovieRef({ id: ..., title: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = upsertMovieRef(connector, upsertMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_upsert); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_upsert); +}); +``` + +## DeleteMovie +You can execute the `DeleteMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +deleteMovie(vars: DeleteMovieVariables): MutationPromise; + +deleteMovieRef(vars: DeleteMovieVariables): (MutationRef & { __angular?: false }); +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; + +deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): (MutationRef & { __angular?: false }); +``` + +### Variables +The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface DeleteMovieVariables { + id: UUIDString; +} +``` +### Return Type +Recall that executing the `DeleteMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `DeleteMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface DeleteMovieData { + movie_delete?: Movie_Key | null; +} +``` +### Using `DeleteMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, deleteMovie, DeleteMovieVariables } from '@dataconnect/default-connector'; +// The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: +const deleteMovieVars: DeleteMovieVariables = { + id: ..., +} + +// Call the `deleteMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await deleteMovie(deleteMovieVars); +// Variables can be defined inline as well. +const { data } = await deleteMovie({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await deleteMovie(connector, deleteMovieVars); + +console.log(data.movie_delete); + +// Or, you can use the `Promise` API. +deleteMovie(deleteMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_delete); +}); +``` + +### Using `DeleteMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, deleteMovieRef, DeleteMovieVariables } from '@dataconnect/default-connector'; +// The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: +const deleteMovieVars: DeleteMovieVariables = { + id: ..., +} + +// Call the `deleteMovieRef()` function to get a reference to the mutation. +const ref = deleteMovieRef(deleteMovieVars); +// Variables can be defined inline as well. +const ref = deleteMovieRef({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = deleteMovieRef(connector, deleteMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_delete); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_delete); +}); +``` + diff --git a/dataconnect-sdk/js/default-connector/esm/index.esm.js b/dataconnect-sdk/js/default-connector/esm/index.esm.js index 96abe7c5..a599e114 100644 --- a/dataconnect-sdk/js/default-connector/esm/index.esm.js +++ b/dataconnect-sdk/js/default-connector/esm/index.esm.js @@ -1,4 +1,5 @@ -import { getDataConnect, queryRef, executeQuery, mutationRef, executeMutation, validateArgs } from 'firebase/data-connect'; +import { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } from 'firebase/data-connect'; + export const connectorConfig = { connector: 'default', @@ -8,61 +9,50 @@ export const connectorConfig = { export function createMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'CreateMovie', inputVars); } + export function createMovie(dcOrVars, vars) { return executeMutation(createMovieRef(dcOrVars, vars)); } + export function upsertMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'UpsertMovie', inputVars); } + export function upsertMovie(dcOrVars, vars) { return executeMutation(upsertMovieRef(dcOrVars, vars)); } + export function deleteMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'DeleteMovie', inputVars); } + export function deleteMovie(dcOrVars, vars) { return executeMutation(deleteMovieRef(dcOrVars, vars)); } + export function listMoviesRef(dc) { const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'ListMovies'); } + export function listMovies(dc) { return executeQuery(listMoviesRef(dc)); } + export function getMovieByIdRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'GetMovieById', inputVars); } + export function getMovieById(dcOrVars, vars) { return executeQuery(getMovieByIdRef(dcOrVars, vars)); } diff --git a/dataconnect-sdk/js/default-connector/index.cjs.js b/dataconnect-sdk/js/default-connector/index.cjs.js index 012bc15e..13685044 100644 --- a/dataconnect-sdk/js/default-connector/index.cjs.js +++ b/dataconnect-sdk/js/default-connector/index.cjs.js @@ -1,4 +1,4 @@ -const { getDataConnect, queryRef, executeQuery, mutationRef, executeMutation, validateArgs } = require('firebase/data-connect'); +const { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } = require('firebase/data-connect'); const connectorConfig = { connector: 'default', @@ -9,70 +9,55 @@ exports.connectorConfig = connectorConfig; function createMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'CreateMovie', inputVars); } exports.createMovieRef = createMovieRef; + exports.createMovie = function createMovie(dcOrVars, vars) { return executeMutation(createMovieRef(dcOrVars, vars)); }; function upsertMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'UpsertMovie', inputVars); } exports.upsertMovieRef = upsertMovieRef; + exports.upsertMovie = function upsertMovie(dcOrVars, vars) { return executeMutation(upsertMovieRef(dcOrVars, vars)); }; function deleteMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'DeleteMovie', inputVars); } exports.deleteMovieRef = deleteMovieRef; + exports.deleteMovie = function deleteMovie(dcOrVars, vars) { return executeMutation(deleteMovieRef(dcOrVars, vars)); }; function listMoviesRef(dc) { const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'ListMovies'); } exports.listMoviesRef = listMoviesRef; + exports.listMovies = function listMovies(dc) { return executeQuery(listMoviesRef(dc)); }; function getMovieByIdRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); - if('_useGeneratedSdk' in dcInstance) { - dcInstance._useGeneratedSdk(); - } else { - console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); - } + dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'GetMovieById', inputVars); } exports.getMovieByIdRef = getMovieByIdRef; + exports.getMovieById = function getMovieById(dcOrVars, vars) { return executeQuery(getMovieByIdRef(dcOrVars, vars)); }; diff --git a/dataconnect-sdk/js/default-connector/index.d.ts b/dataconnect-sdk/js/default-connector/index.d.ts index 2d01de34..a4fd0588 100644 --- a/dataconnect-sdk/js/default-connector/index.d.ts +++ b/dataconnect-sdk/js/default-connector/index.d.ts @@ -1,16 +1,13 @@ import { ConnectorConfig, DataConnect, QueryRef, QueryPromise, MutationRef, MutationPromise } from 'firebase/data-connect'; + export const connectorConfig: ConnectorConfig; export type TimestampString = string; - export type UUIDString = string; - export type Int64String = string; - export type DateString = string; - export interface CreateMovieData { movie_insert: Movie_Key; } @@ -72,48 +69,47 @@ export interface UpsertMovieVariables { } - /* Allow users to create refs without passing in DataConnect */ -export function createMovieRef(vars: CreateMovieVariables): MutationRef; +export function createMovieRef(vars: CreateMovieVariables): (MutationRef & { __angular?: false }); /* Allow users to pass in custom DataConnect instances */ -export function createMovieRef(dc: DataConnect, vars: CreateMovieVariables): MutationRef; +export function createMovieRef(dc: DataConnect, vars: CreateMovieVariables): (MutationRef & { __angular?: false }); export function createMovie(vars: CreateMovieVariables): MutationPromise; -export function createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; +export function createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; /* Allow users to create refs without passing in DataConnect */ -export function upsertMovieRef(vars: UpsertMovieVariables): MutationRef; +export function upsertMovieRef(vars: UpsertMovieVariables): (MutationRef & { __angular?: false }); /* Allow users to pass in custom DataConnect instances */ -export function upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): MutationRef; +export function upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): (MutationRef & { __angular?: false }); export function upsertMovie(vars: UpsertMovieVariables): MutationPromise; -export function upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; +export function upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; /* Allow users to create refs without passing in DataConnect */ -export function deleteMovieRef(vars: DeleteMovieVariables): MutationRef; +export function deleteMovieRef(vars: DeleteMovieVariables): (MutationRef & { __angular?: false }); /* Allow users to pass in custom DataConnect instances */ -export function deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): MutationRef; +export function deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): (MutationRef & { __angular?: false }); export function deleteMovie(vars: DeleteMovieVariables): MutationPromise; -export function deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; +export function deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; /* Allow users to create refs without passing in DataConnect */ -export function listMoviesRef(): QueryRef;/* Allow users to pass in custom DataConnect instances */ -export function listMoviesRef(dc: DataConnect): QueryRef; +export function listMoviesRef(): (QueryRef & { __angular?: false }); +/* Allow users to pass in custom DataConnect instances */ +export function listMoviesRef(dc: DataConnect): (QueryRef & { __angular?: false }); export function listMovies(): QueryPromise; -export function listMovies(dc: DataConnect): QueryPromise; +export function listMovies(dc: DataConnect): QueryPromise; /* Allow users to create refs without passing in DataConnect */ -export function getMovieByIdRef(vars: GetMovieByIdVariables): QueryRef; +export function getMovieByIdRef(vars: GetMovieByIdVariables): (QueryRef & { __angular?: false }); /* Allow users to pass in custom DataConnect instances */ -export function getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; +export function getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): (QueryRef & { __angular?: false }); export function getMovieById(vars: GetMovieByIdVariables): QueryPromise; -export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; - +export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; diff --git a/dataconnect-sdk/js/default-connector/package.json b/dataconnect-sdk/js/default-connector/package.json index faa3fdfb..072295b6 100644 --- a/dataconnect-sdk/js/default-connector/package.json +++ b/dataconnect-sdk/js/default-connector/package.json @@ -17,9 +17,15 @@ "require": "./index.cjs.js", "default": "./esm/index.esm.js" }, + "./react": { + "types": "./react/index.d.ts", + "import": "./react/esm/index.esm.js", + "default": "./react/esm/index.esm.js" + }, "./package.json": "./package.json" }, "peerDependencies": { - "firebase": "^10.14.0 || ^11.3.0" + "firebase": "^11.3.0", + "@tanstack-query-firebase/react": "^1.0.5" } } \ No newline at end of file diff --git a/dataconnect-sdk/js/default-connector/react/esm/index.esm.js b/dataconnect-sdk/js/default-connector/react/esm/index.esm.js new file mode 100644 index 00000000..be7e2b22 --- /dev/null +++ b/dataconnect-sdk/js/default-connector/react/esm/index.esm.js @@ -0,0 +1,40 @@ +import { createMovieRef, upsertMovieRef, deleteMovieRef, listMoviesRef, getMovieByIdRef, connectorConfig } from '../../'; +import { CallerSdkTypeEnum, validateArgs } from '@firebase/data-connect'; +import { useDataConnectQuery, useDataConnectMutation } from '@tanstack-query-firebase/react/data-connect'; + +export function useCreateMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return createMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + +export function useUpsertMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return upsertMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + +export function useDeleteMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return deleteMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + + +export function useListMovies(dc, options) { + const { dc: dcInstance } = validateArgs(connectorConfig, dc, undefined, false); + const ref = listMoviesRef(dcInstance); + return useDataConnectQuery(ref, options, CallerSdkTypeEnum.GeneratedReact); +} + +export function useGetMovieById(dcOrVars, vars, options) { + const { dc: dcInstance, vars: inputVars } = validateArgs(connectorConfig, dcOrVars, vars, false); + const ref = getMovieByIdRef(dcInstance, inputVars); + return useDataConnectQuery(ref, options, CallerSdkTypeEnum.GeneratedReact); +} \ No newline at end of file diff --git a/dataconnect-sdk/js/default-connector/react/esm/package.json b/dataconnect-sdk/js/default-connector/react/esm/package.json new file mode 100644 index 00000000..7c34deb5 --- /dev/null +++ b/dataconnect-sdk/js/default-connector/react/esm/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/dataconnect-sdk/js/default-connector/react/index.cjs.js b/dataconnect-sdk/js/default-connector/react/index.cjs.js new file mode 100644 index 00000000..500a11dc --- /dev/null +++ b/dataconnect-sdk/js/default-connector/react/index.cjs.js @@ -0,0 +1,41 @@ +const { createMovieRef, upsertMovieRef, deleteMovieRef, listMoviesRef, getMovieByIdRef, connectorConfig } = require('../'); +const { CallerSdkTypeEnum, validateArgs } = require('@firebase/data-connect'); +const { useDataConnectQuery, useDataConnectMutation } = require('@tanstack-query-firebase/react/data-connect'); + + +exports.useCreateMovie = function useCreateMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return createMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + +exports.useUpsertMovie = function useUpsertMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return upsertMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + +exports.useDeleteMovie = function useDeleteMovie(dcOrOptions, options) { + const { dc: dcInstance, vars: inputOpts } = validateArgs(connectorConfig, dcOrOptions, options, false); + function refFactory(vars) { + return deleteMovieRef(dcInstance, vars); + } + return useDataConnectMutation(refFactory, inputOpts, CallerSdkTypeEnum.GeneratedReact); +} + + +exports.useListMovies = function useListMovies(dc, options) { + const { dc: dcInstance } = validateArgs(connectorConfig, dc, undefined, false); + const ref = listMoviesRef(dcInstance); + return useDataConnectQuery(ref, options, CallerSdkTypeEnum.GeneratedReact); +} + +exports.useGetMovieById = function useGetMovieById(dcOrVars, vars, options) { + const { dc: dcInstance, vars: inputVars } = validateArgs(connectorConfig, dcOrVars, vars, false); + const ref = getMovieByIdRef(dcInstance, inputVars); + return useDataConnectQuery(ref, options, CallerSdkTypeEnum.GeneratedReact); +} \ No newline at end of file diff --git a/dataconnect-sdk/js/default-connector/react/index.d.ts b/dataconnect-sdk/js/default-connector/react/index.d.ts new file mode 100644 index 00000000..bbd50c8c --- /dev/null +++ b/dataconnect-sdk/js/default-connector/react/index.d.ts @@ -0,0 +1,20 @@ +import { CreateMovieData, CreateMovieVariables, UpsertMovieData, UpsertMovieVariables, DeleteMovieData, DeleteMovieVariables, ListMoviesData, GetMovieByIdData, GetMovieByIdVariables} from '../'; +import { useDataConnectQueryOptions, FlattenedQueryResult, useDataConnectMutationOptions, FlattenedMutationResult} from '@tanstack-query-firebase/react/data-connect'; +import { UseQueryResult, UseMutationResult} from '@tanstack/react-query'; +import { DataConnect } from 'firebase/data-connect'; + + +export function useCreateMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, CreateMovieVariables>; +export function useCreateMovie(dc: DataConnect, options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, CreateMovieVariables>; + +export function useUpsertMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, UpsertMovieVariables>; +export function useUpsertMovie(dc: DataConnect, options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, UpsertMovieVariables>; + +export function useDeleteMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, DeleteMovieVariables>; +export function useDeleteMovie(dc: DataConnect, options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, DeleteMovieVariables>; + +export function useListMovies(options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; +export function useListMovies(dc: DataConnect, options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; + +export function useGetMovieById(vars: GetMovieByIdVariables, options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; +export function useGetMovieById(dc: DataConnect, vars: GetMovieByIdVariables, options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; diff --git a/dataconnect-sdk/js/default-connector/react/package.json b/dataconnect-sdk/js/default-connector/react/package.json new file mode 100644 index 00000000..f2fee700 --- /dev/null +++ b/dataconnect-sdk/js/default-connector/react/package.json @@ -0,0 +1,17 @@ +{ + "name": "@dataconnect/default-connector-react", + "version": "1.0.0", + "author": "Firebase (https://firebase.google.com/)", + "description": "Generated SDK For default", + "license": "Apache-2.0", + "engines": { + "node": " >=18.0" + }, + "typings": "index.d.ts", + "main": "index.cjs.js", + "module": "esm/index.esm.js", + "browser": "esm/index.esm.js", + "peerDependencies": { + "@tanstack-query-firebase/react": "^1.0.5" + } +} \ No newline at end of file diff --git a/dataconnect/.dataconnect/schema/main/implicit.gql b/dataconnect/.dataconnect/schema/main/implicit.gql deleted file mode 100644 index b4fc81d7..00000000 --- a/dataconnect/.dataconnect/schema/main/implicit.gql +++ /dev/null @@ -1,10 +0,0 @@ -extend type MovieMetadata { - """ - ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. - """ - id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "MovieMetadata", purpose: IMPLICIT_KEY_FIELD) - """ - ✨ Implicit foreign key field based on `MovieMetadata`.`movie`. It must match the value of `Movie`.`id`. See `@ref` for how to customize it. - """ - movieId: UUID! @fdc_generated(from: "MovieMetadata.movie", purpose: IMPLICIT_REF_FIELD) -} diff --git a/dataconnect/.dataconnect/schema/main/input.gql b/dataconnect/.dataconnect/schema/main/input.gql deleted file mode 100644 index 0b662a21..00000000 --- a/dataconnect/.dataconnect/schema/main/input.gql +++ /dev/null @@ -1,296 +0,0 @@ -""" -✨ `Movie_KeyOutput` returns the primary key fields of table type `Movie`. - -It has the same format as `Movie_Key`, but is only used as mutation return value. -""" -scalar Movie_KeyOutput -""" -✨ `MovieMetadata_KeyOutput` returns the primary key fields of table type `MovieMetadata`. - -It has the same format as `MovieMetadata_Key`, but is only used as mutation return value. -""" -scalar MovieMetadata_KeyOutput -""" -✨ Generated data input type for table 'Movie'. It includes all necessary fields for creating or upserting rows into table. -""" -input Movie_Data { - """ - ✨ Generated from Field `Movie`.`id` of type `UUID!` - """ - id: UUID - """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) - """ - id_expr: UUID_Expr - """ - ✨ Generated from Field `Movie`.`genre` of type `String` - """ - genre: String - """ - ✨ `_expr` server value variant of `genre` (✨ Generated from Field `Movie`.`genre` of type `String`) - """ - genre_expr: String_Expr - """ - ✨ Generated from Field `Movie`.`imageUrl` of type `String!` - """ - imageUrl: String - """ - ✨ `_expr` server value variant of `imageUrl` (✨ Generated from Field `Movie`.`imageUrl` of type `String!`) - """ - imageUrl_expr: String_Expr - """ - ✨ Generated from Field `Movie`.`title` of type `String!` - """ - title: String - """ - ✨ `_expr` server value variant of `title` (✨ Generated from Field `Movie`.`title` of type `String!`) - """ - title_expr: String_Expr -} -""" -✨ Generated filter input type for table 'Movie'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. -""" -input Movie_Filter { - """ - Apply multiple filter conditions using `AND` logic. - """ - _and: [Movie_Filter!] - """ - Negate the result of the provided filter condition. - """ - _not: Movie_Filter - """ - Apply multiple filter conditions using `OR` logic. - """ - _or: [Movie_Filter!] - """ - ✨ Generated from Field `Movie`.`id` of type `UUID!` - """ - id: UUID_Filter - """ - ✨ Generated from Field `Movie`.`genre` of type `String` - """ - genre: String_Filter - """ - ✨ Generated from Field `Movie`.`imageUrl` of type `String!` - """ - imageUrl: String_Filter - """ - ✨ Generated from Field `Movie`.`title` of type `String!` - """ - title: String_Filter - """ - ✨ Generated from Field `Movie`.`movieMetadata_on_movie` of type `MovieMetadata` - """ - movieMetadata_on_movie: MovieMetadata_Filter -} -""" -✨ Generated first-row input type for table 'Movie'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. -""" -input Movie_FirstRow { - """ - Order the result by the specified fields. - """ - orderBy: [Movie_Order!] - """ - Filters rows based on the specified conditions. - """ - where: Movie_Filter -} -""" -✨ Generated key input type for table 'Movie'. It represents the primary key fields used to uniquely identify a row in the table. -""" -input Movie_Key { - """ - ✨ Generated from Field `Movie`.`id` of type `UUID!` - """ - id: UUID - """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) - """ - id_expr: UUID_Expr -} -""" -✨ Generated list filter input type for table 'Movie'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. -""" -input Movie_ListFilter { - """ - The desired number of objects that match the condition (defaults to at least one). - """ - count: Int_Filter = {gt:0} - """ - Condition of the related objects to filter for. - """ - exist: Movie_Filter -} -""" -✨ Generated order input type for table 'Movie'. This input defines the sorting order of rows in query results based on one or more fields. -""" -input Movie_Order { - """ - ✨ Generated from Field `Movie`.`id` of type `UUID!` - """ - id: OrderDirection - """ - ✨ Generated from Field `Movie`.`genre` of type `String` - """ - genre: OrderDirection - """ - ✨ Generated from Field `Movie`.`imageUrl` of type `String!` - """ - imageUrl: OrderDirection - """ - ✨ Generated from Field `Movie`.`title` of type `String!` - """ - title: OrderDirection -} -""" -✨ Generated data input type for table 'MovieMetadata'. It includes all necessary fields for creating or upserting rows into table. -""" -input MovieMetadata_Data { - """ - ✨ Generated from Field `MovieMetadata`.`id` of type `UUID!` - """ - id: UUID - """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `MovieMetadata`.`id` of type `UUID!`) - """ - id_expr: UUID_Expr - """ - ✨ Generated from Field `MovieMetadata`.`movieId` of type `UUID!` - """ - movieId: UUID - """ - ✨ `_expr` server value variant of `movieId` (✨ Generated from Field `MovieMetadata`.`movieId` of type `UUID!`) - """ - movieId_expr: UUID_Expr - """ - ✨ Generated from Field `MovieMetadata`.`movie` of type `Movie!` - """ - movie: Movie_Key - """ - ✨ Generated from Field `MovieMetadata`.`description` of type `String` - """ - description: String - """ - ✨ `_expr` server value variant of `description` (✨ Generated from Field `MovieMetadata`.`description` of type `String`) - """ - description_expr: String_Expr - """ - ✨ Generated from Field `MovieMetadata`.`rating` of type `Float` - """ - rating: Float - """ - ✨ Generated from Field `MovieMetadata`.`releaseYear` of type `Int` - """ - releaseYear: Int -} -""" -✨ Generated filter input type for table 'MovieMetadata'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. -""" -input MovieMetadata_Filter { - """ - Apply multiple filter conditions using `AND` logic. - """ - _and: [MovieMetadata_Filter!] - """ - Negate the result of the provided filter condition. - """ - _not: MovieMetadata_Filter - """ - Apply multiple filter conditions using `OR` logic. - """ - _or: [MovieMetadata_Filter!] - """ - ✨ Generated from Field `MovieMetadata`.`id` of type `UUID!` - """ - id: UUID_Filter - """ - ✨ Generated from Field `MovieMetadata`.`movieId` of type `UUID!` - """ - movieId: UUID_Filter - """ - ✨ Generated from Field `MovieMetadata`.`movie` of type `Movie!` - """ - movie: Movie_Filter - """ - ✨ Generated from Field `MovieMetadata`.`description` of type `String` - """ - description: String_Filter - """ - ✨ Generated from Field `MovieMetadata`.`rating` of type `Float` - """ - rating: Float_Filter - """ - ✨ Generated from Field `MovieMetadata`.`releaseYear` of type `Int` - """ - releaseYear: Int_Filter -} -""" -✨ Generated first-row input type for table 'MovieMetadata'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. -""" -input MovieMetadata_FirstRow { - """ - Order the result by the specified fields. - """ - orderBy: [MovieMetadata_Order!] - """ - Filters rows based on the specified conditions. - """ - where: MovieMetadata_Filter -} -""" -✨ Generated key input type for table 'MovieMetadata'. It represents the primary key fields used to uniquely identify a row in the table. -""" -input MovieMetadata_Key { - """ - ✨ Generated from Field `MovieMetadata`.`id` of type `UUID!` - """ - id: UUID - """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `MovieMetadata`.`id` of type `UUID!`) - """ - id_expr: UUID_Expr -} -""" -✨ Generated list filter input type for table 'MovieMetadata'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. -""" -input MovieMetadata_ListFilter { - """ - The desired number of objects that match the condition (defaults to at least one). - """ - count: Int_Filter = {gt:0} - """ - Condition of the related objects to filter for. - """ - exist: MovieMetadata_Filter -} -""" -✨ Generated order input type for table 'MovieMetadata'. This input defines the sorting order of rows in query results based on one or more fields. -""" -input MovieMetadata_Order { - """ - ✨ Generated from Field `MovieMetadata`.`id` of type `UUID!` - """ - id: OrderDirection - """ - ✨ Generated from Field `MovieMetadata`.`movieId` of type `UUID!` - """ - movieId: OrderDirection - """ - ✨ Generated from Field `MovieMetadata`.`movie` of type `Movie!` - """ - movie: Movie_Order - """ - ✨ Generated from Field `MovieMetadata`.`description` of type `String` - """ - description: OrderDirection - """ - ✨ Generated from Field `MovieMetadata`.`rating` of type `Float` - """ - rating: OrderDirection - """ - ✨ Generated from Field `MovieMetadata`.`releaseYear` of type `Int` - """ - releaseYear: OrderDirection -} diff --git a/dataconnect/.dataconnect/schema/main/mutation.gql b/dataconnect/.dataconnect/schema/main/mutation.gql deleted file mode 100644 index 111464cb..00000000 --- a/dataconnect/.dataconnect/schema/main/mutation.gql +++ /dev/null @@ -1,226 +0,0 @@ -extend type Mutation { - """ - ✨ Insert a single `Movie` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). - """ - movie_insert( - """ - Data object to insert into the table. - """ - data: Movie_Data! - ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: INSERT_SINGLE) - """ - ✨ Insert a single `MovieMetadata` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). - """ - movieMetadata_insert( - """ - Data object to insert into the table. - """ - data: MovieMetadata_Data! - ): MovieMetadata_KeyOutput! @fdc_generated(from: "MovieMetadata", purpose: INSERT_SINGLE) - """ - ✨ Insert `Movie` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). - """ - movie_insertMany( - """ - List of data objects to insert into the table. - """ - data: [Movie_Data!]! - ): [Movie_KeyOutput!]! @fdc_generated(from: "Movie", purpose: INSERT_MULTIPLE) - """ - ✨ Insert `MovieMetadata` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). - """ - movieMetadata_insertMany( - """ - List of data objects to insert into the table. - """ - data: [MovieMetadata_Data!]! - ): [MovieMetadata_KeyOutput!]! @fdc_generated(from: "MovieMetadata", purpose: INSERT_MULTIPLE) - """ - ✨ Insert or update a single `Movie` into the table, based on the primary key. Returns the key of the newly inserted `Movie`. - """ - movie_upsert( - """ - Data object to insert or update if it already exists. - """ - data: Movie_Data! - ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: UPSERT_SINGLE) - """ - ✨ Insert or update a single `MovieMetadata` into the table, based on the primary key. Returns the key of the newly inserted `MovieMetadata`. - """ - movieMetadata_upsert( - """ - Data object to insert or update if it already exists. - """ - data: MovieMetadata_Data! - ): MovieMetadata_KeyOutput! @fdc_generated(from: "MovieMetadata", purpose: UPSERT_SINGLE) - """ - ✨ Insert or update `Movie` objects into the table, based on the primary key. Returns the key of the newly inserted `Movie`. - """ - movie_upsertMany( - """ - List of data objects to insert or update if it already exists. - """ - data: [Movie_Data!]! - ): [Movie_KeyOutput!]! @fdc_generated(from: "Movie", purpose: UPSERT_MULTIPLE) - """ - ✨ Insert or update `MovieMetadata` objects into the table, based on the primary key. Returns the key of the newly inserted `MovieMetadata`. - """ - movieMetadata_upsertMany( - """ - List of data objects to insert or update if it already exists. - """ - data: [MovieMetadata_Data!]! - ): [MovieMetadata_KeyOutput!]! @fdc_generated(from: "MovieMetadata", purpose: UPSERT_MULTIPLE) - """ - ✨ Update a single `Movie` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. - """ - movie_update( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: Movie_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: Movie_FirstRow - - """ - Data object containing fields to be updated. - """ - data: Movie_Data! - ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: UPDATE_SINGLE) - """ - ✨ Update a single `MovieMetadata` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. - """ - movieMetadata_update( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: MovieMetadata_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: MovieMetadata_FirstRow - - """ - Data object containing fields to be updated. - """ - data: MovieMetadata_Data! - ): MovieMetadata_KeyOutput @fdc_generated(from: "MovieMetadata", purpose: UPDATE_SINGLE) - """ - ✨ Update `Movie` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. - """ - movie_updateMany( - """ - Filter condition to specify which rows to update. - """ - where: Movie_Filter - - """ - Set to true to update all rows. - """ - all: Boolean = false - - """ - Data object containing fields to update. - """ - data: Movie_Data! - ): Int! @fdc_generated(from: "Movie", purpose: UPDATE_MULTIPLE) - """ - ✨ Update `MovieMetadata` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. - """ - movieMetadata_updateMany( - """ - Filter condition to specify which rows to update. - """ - where: MovieMetadata_Filter - - """ - Set to true to update all rows. - """ - all: Boolean = false - - """ - Data object containing fields to update. - """ - data: MovieMetadata_Data! - ): Int! @fdc_generated(from: "MovieMetadata", purpose: UPDATE_MULTIPLE) - """ - ✨ Delete a single `Movie` based on `id`, `key` or `first` and return its key (or `null` if not found). - """ - movie_delete( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: Movie_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: Movie_FirstRow - ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: DELETE_SINGLE) - """ - ✨ Delete a single `MovieMetadata` based on `id`, `key` or `first` and return its key (or `null` if not found). - """ - movieMetadata_delete( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: MovieMetadata_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: MovieMetadata_FirstRow - ): MovieMetadata_KeyOutput @fdc_generated(from: "MovieMetadata", purpose: DELETE_SINGLE) - """ - ✨ Delete `Movie` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. - """ - movie_deleteMany( - """ - Filter condition to specify which rows to delete. - """ - where: Movie_Filter - - """ - Set to true to delete all rows. - """ - all: Boolean = false - ): Int! @fdc_generated(from: "Movie", purpose: DELETE_MULTIPLE) - """ - ✨ Delete `MovieMetadata` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. - """ - movieMetadata_deleteMany( - """ - Filter condition to specify which rows to delete. - """ - where: MovieMetadata_Filter - - """ - Set to true to delete all rows. - """ - all: Boolean = false - ): Int! @fdc_generated(from: "MovieMetadata", purpose: DELETE_MULTIPLE) -} diff --git a/dataconnect/.dataconnect/schema/main/query.gql b/dataconnect/.dataconnect/schema/main/query.gql deleted file mode 100644 index 899898d1..00000000 --- a/dataconnect/.dataconnect/schema/main/query.gql +++ /dev/null @@ -1,88 +0,0 @@ -extend type Query { - """ - ✨ Look up a single `Movie` based on `id`, `key` or `first` and return selected fields (or `null` if not found). - """ - movie( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: Movie_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: Movie_FirstRow - ): Movie @fdc_generated(from: "Movie", purpose: QUERY_SINGLE) - """ - ✨ Look up a single `MovieMetadata` based on `id`, `key` or `first` and return selected fields (or `null` if not found). - """ - movieMetadata( - """ - The unique ID of the object. - """ - id: UUID - - """ - The key used to identify the object. - """ - key: MovieMetadata_Key - - """ - Fetch the first row based on the filters and ordering. - """ - first: MovieMetadata_FirstRow - ): MovieMetadata @fdc_generated(from: "MovieMetadata", purpose: QUERY_SINGLE) - """ - ✨ List `Movie` objects in the table, optionally filtered by `where` conditions. - """ - movies( - """ - Filter condition to narrow down the query results. - """ - where: Movie_Filter - - """ - Order the query results by specific fields. - """ - orderBy: [Movie_Order!] - - """ - Number of rows to skip before starting to return the results. - """ - offset: Int - - """ - Maximum number of rows to return (defaults to 100 rows). - """ - limit: Int = 100 - ): [Movie!]! @fdc_generated(from: "Movie", purpose: QUERY_MULTIPLE) - """ - ✨ List `MovieMetadata` objects in the table, optionally filtered by `where` conditions. - """ - movieMetadatas( - """ - Filter condition to narrow down the query results. - """ - where: MovieMetadata_Filter - - """ - Order the query results by specific fields. - """ - orderBy: [MovieMetadata_Order!] - - """ - Number of rows to skip before starting to return the results. - """ - offset: Int - - """ - Maximum number of rows to return (defaults to 100 rows). - """ - limit: Int = 100 - ): [MovieMetadata!]! @fdc_generated(from: "MovieMetadata", purpose: QUERY_MULTIPLE) -} diff --git a/dataconnect/.dataconnect/schema/main/relation.gql b/dataconnect/.dataconnect/schema/main/relation.gql deleted file mode 100644 index c14ce6cc..00000000 --- a/dataconnect/.dataconnect/schema/main/relation.gql +++ /dev/null @@ -1,6 +0,0 @@ -extend type Movie { - """ - ✨ List `MovieMetadata` objects in a one-to-one relationship (where `MovieMetadata`.`movie` is this object). - """ - movieMetadata_on_movie: MovieMetadata @fdc_generated(from: "MovieMetadata.movie", purpose: QUERY_SINGLE_ONE_TO_ONE) -} diff --git a/dataconnect/.dataconnect/schema/prelude.gql b/dataconnect/.dataconnect/schema/prelude.gql deleted file mode 100644 index bc21c044..00000000 --- a/dataconnect/.dataconnect/schema/prelude.gql +++ /dev/null @@ -1,2006 +0,0 @@ -"AccessLevel specifies coarse access policies for common situations." -enum AccessLevel { - """ - This operation is accessible to anyone, with or without authentication. - Equivalent to: `@auth(expr: "true")` - """ - PUBLIC - - """ - This operation can be executed only with a valid Firebase Auth ID token. - **Note:** This access level allows anonymous and unverified accounts, - which may present security and abuse risks. - Equivalent to: `@auth(expr: "auth.uid != nil")` - """ - USER_ANON - - """ - This operation is restricted to non-anonymous Firebase Auth accounts. - Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")` - """ - USER - - """ - This operation is restricted to Firebase Auth accounts with verified email addresses. - Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.email_verified")` - """ - USER_EMAIL_VERIFIED - - """ - This operation cannot be executed by anyone. The operation can only be performed - by using the Admin SDK from a privileged environment. - Equivalent to: `@auth(expr: "false")` - """ - NO_ACCESS -} - -""" -The `@auth` directive defines the authentication policy for a query or mutation. - -It must be added to any operation that you wish to be accessible from a client -application. If not specified, the operation defaults to `@auth(level: NO_ACCESS)`. - -Refer to [Data Connect Auth Guide](https://firebase.google.com/docs/data-connect/authorization-and-security) for the best practices. -""" -directive @auth( - """ - The minimal level of access required to perform this operation. - Exactly one of `level` and `expr` should be specified. - """ - level: AccessLevel @fdc_oneOf(required: true) - """ - A CEL expression that grants access to this operation if the expression - evaluates to `true`. - Exactly one of `level` and `expr` should be specified. - """ - expr: Boolean_Expr @fdc_oneOf(required: true) -) on QUERY | MUTATION - - -""" -Require that this mutation always run in a DB transaction. - -Mutations with `@transaction` are guaranteed to either fully succeed or fully -fail. If any of the fields within the transaction fails, the entire transaction -is rolled back. From a client standpoint, any failure behaves as if the entire -request had failed with a request error and execution had not begun. - -Mutations without `@transaction` would execute each root field one after -another in sequence. It surfaces any errors as partial [field errors](https://spec.graphql.org/October2021/#sec-Errors.Field-errors), -but not impacts the subsequent executions. - -The `@transaction` directive cannot be added to queries for now. -Currently, queries cannot fail partially, the response data is not guaranteed -to be a consistent snapshot. -""" -directive @transaction on MUTATION - -"Query filter criteria for `String` scalar fields." -input String_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: String @fdc_oneOf(group: "eq") - """ - Match if field is exactly equal to the result of the provided server value - expression. Currently only `auth.uid` is supported as an expression. - """ - eq_expr: String_Expr @fdc_oneOf(group: "eq") - "Match if field is not equal to provided value." - ne: String @fdc_oneOf(group: "ne") - """ - Match if field is not equal to the result of the provided server value - expression. Currently only `auth.uid` is supported as an expression. - """ - ne_expr: String_Expr @fdc_oneOf(group: "ne") - "Match if field value is among the provided list of values." - in: [String!] - "Match if field value is not among the provided list of values." - nin: [String!] - "Match if field value is greater than the provided value." - gt: String - "Match if field value is greater than or equal to the provided value." - ge: String - "Match if field value is less than the provided value." - lt: String - "Match if field value is less than or equal to the provided value." - le: String - """ - Match if field value contains the provided value as a substring. Equivalent - to `LIKE '%value%'` - """ - contains: String - """ - Match if field value starts with the provided value. Equivalent to - `LIKE 'value%'` - """ - startsWith: String - """ - Match if field value ends with the provided value. Equivalent to - `LIKE '%value'` - """ - endsWith: String -} - -"Query filter criteris for `[String!]` scalar fields." -input String_ListFilter { - "Match if list field contains the provided value as a member." - includes: String - "Match if list field does not contain the provided value as a member." - excludes: String - "Match if list field contains all of the provided values as members." - includesAll: [String!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [String!] -} - -"Query filter criteria for `UUID` scalar fields." -input UUID_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: UUID - "Match if field is not equal to provided value." - ne: UUID - "Match if field value is among the provided list of values." - in: [UUID!] - "Match if field value is not among the provided list of values." - nin: [UUID!] -} - -"Query filter criteris for `[UUID!]` scalar fields." -input UUID_ListFilter { - "Match if list field contains the provided value as a member." - includes: UUID - "Match if list field does not contain the provided value as a member." - excludes: UUID - "Match if list field contains all of the provided values as members." - includesAll: [UUID!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [UUID!] -} - -"Query filter criteria for `Int` scalar fields." -input Int_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: Int - "Match if field is not equal to provided value." - ne: Int - "Match if field value is among the provided list of values." - in: [Int!] - "Match if field value is not among the provided list of values." - nin: [Int!] - "Match if field value is greater than the provided value." - gt: Int - "Match if field value is greater than or equal to the provided value." - ge: Int - "Match if field value is less than the provided value." - lt: Int - "Match if field value is less than or equal to the provided value." - le: Int -} - -"Query filter criteris for `[Int!]` scalar fields." -input Int_ListFilter { - "Match if list field contains the provided value as a member." - includes: Int - "Match if list field does not contain the provided value as a member." - excludes: Int - "Match if list field contains all of the provided values as members." - includesAll: [Int!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [Int!] -} - -"Query filter criteria for `Int64` scalar fields." -input Int64_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: Int64 - "Match if field is not equal to provided value." - ne: Int64 - "Match if field value is among the provided list of values." - in: [Int64!] - "Match if field value is not among the provided list of values." - nin: [Int64!] - "Match if field value is greater than the provided value." - gt: Int64 - "Match if field value is greater than or equal to the provided value." - ge: Int64 - "Match if field value is less than the provided value." - lt: Int64 - "Match if field value is less than or equal to the provided value." - le: Int64 -} - -"Query filter criteria for `[Int64!]` scalar fields." -input Int64_ListFilter { - "Match if list field contains the provided value as a member." - includes: Int64 - "Match if list field does not contain the provided value as a member." - excludes: Int64 - "Match if list field contains all of the provided values as members." - includesAll: [Int64!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [Int64!] -} - -"Query filter criteria for `Float` scalar fields." -input Float_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: Float - "Match if field is not equal to provided value." - ne: Float - "Match if field value is among the provided list of values." - in: [Float!] - "Match if field value is not among the provided list of values." - nin: [Float!] - "Match if field value is greater than the provided value." - gt: Float - "Match if field value is greater than or equal to the provided value." - ge: Float - "Match if field value is less than the provided value." - lt: Float - "Match if field value is less than or equal to the provided value." - le: Float -} - -"Query filter criteria for `[Float!]` scalar fields." -input Float_ListFilter { - "Match if list field contains the provided value as a member." - includes: Float - "Match if list field does not contain the provided value as a member." - excludes: Float - "Match if list field contains all of the provided values as members." - includesAll: [Float!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [Float!] -} - -"Query filter criteria for `Boolean` scalar fields." -input Boolean_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: Boolean - "Match if field is not equal to provided value." - ne: Boolean - "Match if field value is among the provided list of values." - in: [Boolean!] - "Match if field value is not among the provided list of values." - nin: [Boolean!] -} - -"Query filter criteria for `[Boolean!]` scalar fields." -input Boolean_ListFilter { - "Match if list field contains the provided value as a member." - includes: Boolean - "Match if list field does not contain the provided value as a member." - excludes: Boolean - "Match if list field contains all of the provided values as members." - includesAll: [Boolean!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [Boolean!] -} - -"Query filter criteria for `Any` scalar fields." -input Any_Filter { - "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." - isNull: Boolean - "Match if field is exactly equal to provided value." - eq: Any - "Match if field is not equal to provided value." - ne: Any - "Match if field value is among the provided list of values." - in: [Any!] - "Match if field value is not among the provided list of values." - nin: [Any!] -} - -"Query filter criteria for `[Any!]` scalar fields." -input Any_ListFilter { - "Match if list field contains the provided value as a member." - includes: Any - "Match if list field does not contain the provided value as a member." - excludes: Any - "Match if list field contains all of the provided values as members." - includesAll: [Any!] - "Match if list field does not contain any of the provided values as members." - excludesAll: [Any!] -} - -""" -(Internal) A string that uniquely identifies a type, field, and so on. - -The most common usage in FDC is `SomeType` or `SomeType.someField`. See the -linked page in the @specifiedBy directive for the GraphQL RFC with more details. -""" -scalar SchemaCoordinate - @specifiedBy(url: "https://github.com/graphql/graphql-wg/blob/6d02705dea034fb65ebc6799632adb7bd550d0aa/rfcs/SchemaCoordinates.md") - @fdc_forbiddenAsFieldType - @fdc_forbiddenAsVariableType - -"(Internal) The purpose of a generated type or field." -enum GeneratedPurpose { - # Implicit fields added to the table types as columns. - IMPLICIT_KEY_FIELD - IMPLICIT_REF_FIELD - - # Relational non-column fields extended to table types. - QUERY_MULTIPLE_ONE_TO_MANY - QUERY_MULTIPLE_MANY_TO_MANY - - # Top-level Query fields. - QUERY_SINGLE - QUERY_MULTIPLE - QUERY_MULTIPLE_BY_SIMILARITY - - # Top-level Mutation fields. - INSERT_SINGLE - INSERT_MULTIPLE - UPSERT_SINGLE - UPSERT_MULTIPLE - UPDATE_SINGLE - UPDATE_MULTIPLE - DELETE_SINGLE - DELETE_MULTIPLE -} - -"(Internal) Added to definitions generated by FDC." -directive @fdc_generated( - "The source type or field that causes this definition to be generated." - from: SchemaCoordinate! - "The reason why this definition is generated, such as the intended use case." - purpose: GeneratedPurpose! -) on - | SCALAR - | OBJECT - | FIELD_DEFINITION - | ARGUMENT_DEFINITION - | INTERFACE - | UNION - | ENUM - | ENUM_VALUE - | INPUT_OBJECT - | INPUT_FIELD_DEFINITION - -type _Service { - "Full Service Definition Language of the Frebase Data Connect Schema, including normalized schema, predefined and generated types." - sdl( - """ - Whether or not to omit Data Connect builtin GraphQL preludes. - They are static GraphQL publically available in the docsite. - """ - omitBuiltin: Boolean = false - """ - Whether or not to omit GQL description in the SDL. - We generate description to document generated schema. - It may bloat the size of SDL. - """ - omitDescription: Boolean = false - ): String! - "Orignal Schema Sources in the service." - schema: String! - "Generated documentation from the schema of the Firebase Data Connect Service." - docs: [_Doc!]! -} - -type _Doc { - "Name of the Doc Page." - page: String! - "The markdown content of the doc page." - markdown: String! -} - -"(Internal) Added to things that may be removed from FDC and will soon be no longer usable in schema or operations." -directive @fdc_deprecated(reason: String = "No longer supported") on - | SCHEMA - | SCALAR - | OBJECT - | FIELD_DEFINITION - | ARGUMENT_DEFINITION - | INTERFACE - | UNION - | ENUM - | ENUM_VALUE - | INPUT_OBJECT - | INPUT_FIELD_DEFINITION - -"(Internal) Added to scalars representing quoted CEL expressions." -directive @fdc_celExpression( - "The expected CEL type that the expression should evaluate to." - returnType: String -) on SCALAR - -"(Internal) Added to scalars representing quoted SQL expressions." -directive @fdc_sqlExpression( - "The expected SQL type that the expression should evaluate to." - dataType: String -) on SCALAR - -"(Internal) Added to types that may not be used as variables." -directive @fdc_forbiddenAsVariableType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT - -"(Internal) Added to types that may not be used as fields in schema." -directive @fdc_forbiddenAsFieldType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT - -"Provides a frequently used example for this type / field / argument." -directive @fdc_example( - "A GraphQL literal value (verbatim) whose type matches the target." - value: Any - "A human-readable text description of what `value` means in this context." - description: String -) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | INPUT_OBJECT | INPUT_FIELD_DEFINITION - -"(Internal) Marks this field / argument as conflicting with others in the same group." -directive @fdc_oneOf( - "The group name where fields / arguments conflict with each other." - group: String! = "" - "If true, exactly one field / argument in the group must be specified." - required: Boolean! = false -) repeatable on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION - -""" -`UUID` is a string of hexadecimal digits representing an RFC4122-compliant UUID. - -UUIDs are always output as 32 lowercase hexadecimal digits without delimiters or -curly braces. -Inputs in the following formats are also accepted (case insensitive): - -- `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` -- `urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` -- `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` - -In the PostgreSQL table, it's stored as [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). -""" -scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") - -""" -`Int64` is a scalar that represents a 64-bit signed integer. - -In the PostgreSQL table, it's stored as [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html). - -On the wire, it's encoded as string because 64-bit integer exceeds the range of JSON number. -""" -scalar Int64 - -""" -The `Any` scalar type accommodates any valid [JSON value](https://www.json.org/json-en.html) -(e.g., numbers, strings, booleans, arrays, objects). PostgreSQL efficiently -stores this data as jsonb, providing flexibility for schemas with evolving structures. - -Caution: JSON doesn't distinguish Int and Float. - -##### Example: - -#### Schema - -```graphql -type Movie @table { - name: String! - metadata: Any! -} -``` - -#### Mutation - -Insert a movie with name and metadata from JSON literal. - -```graphql -mutation InsertMovie { - movie_insert( - data: { - name: "The Dark Knight" - metadata: { - release_year: 2008 - genre: ["Action", "Adventure", "Superhero"] - cast: [ - { name: "Christopher Bale", age: 31 } - { name: "Heath Ledger", age: 28 } - ] - director: "Christopher Nolan" - } - } - ) -} -``` - -Insert a movie with name and metadata that's constructed from a few GQL variables. - -```graphql -mutation InsertMovie($name: String!, $releaseDate: Date!, $genre: [String], $cast: [Any], $director: String!, $boxOfficeInUSD: Int) { - movie_insert(data: { - name: $name, - release_date: $releaseDate, - genre: $genre, - cast: $cast, - director: $director, - box_office: $boxOfficeInUSD - }) -} -``` -**Note**: - - - A mix of non-null and nullable variables can be provided. - - - `Date!` can be passed into scalar `Any` as well! It's stored as string. - - - `$cast` is a nested array. `[Any]` can represent an array of arbitrary types, but it won't enforce the input shape. - -#### Query - -Since `metadata` field has scalar `Any` type, it would return the full JSON in the response. - -**Note**: You can't define selection set to scalar based on [GraphQL spec](https://spec.graphql.org/October2021/#sec-Field-Selections). - -```graphql -query GetAllMovies { - movies { - name - metadata - } -} -``` - -""" -scalar Any @specifiedBy(url: "https://www.json.org/json-en.html") - -""" -The `Void` scalar type represents the absence of any value. It is typically used -in operations where no value is expected in return. -""" -scalar Void - -""" -The `True` scalar type only accepts the boolean value `true`. - -An optional field/argument typed as `True` may either be set -to `true` or omitted (not provided at all). The values `false` or `null` are not -accepted. -""" -scalar True - @fdc_forbiddenAsFieldType - @fdc_forbiddenAsVariableType - @fdc_example(value: true, description: "The only allowed value.") - -""" -A Common Expression Language (CEL) expression that returns a boolean at runtime. - -This expression can reference the `auth` variable, which is null when Firebase -Auth is not used. When Firebase Auth is used, the following fields are available: - - - `auth.uid`: The current user ID. - - `auth.token`: A map containing all token fields (e.g., claims). - -""" -scalar Boolean_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression(returnType: "bool") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "auth != null", description: "Allow only if a Firebase Auth user is present.") - -""" -A Common Expression Language (CEL) expression that returns a string at runtime. - -**Limitation**: Currently, only a limited set of expressions are supported. -""" -scalar String_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression(returnType: "string") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "auth.uid", description: "The ID of the currently logged in user in Firebase Auth. (Errors if not logged in.)") - @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) string, formatted as 32 lower-case hex digits without delimiters.") - -""" -A Common Expression Language (CEL) expression that returns a UUID string at runtime. - -**Limitation**: Currently, only a limited set of expressions are supported. -""" -scalar UUID_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression(returnType: "string") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) every time.") - -""" -A Common Expression Language (CEL) expression whose return type is unspecified. - -**Limitation**: Only a limited set of expressions are currently supported for each -type. -""" -scalar Any_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "auth.uid", description: "The ID of the currently logged in user in Firebase Auth. (Errors if not logged in.)") - @fdc_example(value: "uuidV4()", description: "Generates a new random UUID version 4 (formatted as 32 lower-case hex digits without delimiters if result type is String).") - @fdc_example(value: "request.time", description: "The timestamp when the request is received (with microseconds precision).") - -""" -A PostgreSQL value expression whose return type is unspecified. -""" -scalar Any_SQL - @specifiedBy(url: "https://www.postgresql.org/docs/current/sql-expressions.html") - @fdc_sqlExpression - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - -""" -Defines a relational database table. - -In this example, we defined one table with a field named `myField`. - -```graphql -type TableName @table { - myField: String -} -``` -Data Connect adds an implicit `id` primary key column. So the above schema is equivalent to: - -```graphql -type TableName @table(key: "id") { - id: String @default(expr: "uuidV4()") - myField: String -} -``` - -Data Connect generates the following SQL table and CRUD operations to use it. - -```sql -CREATE TABLE "public"."table_name" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "my_field" text NULL, - PRIMARY KEY ("id") -) -``` - - * You can lookup a row: `query ($id: UUID!) { tableName(id: $id) { myField } } ` - * You can find rows using: `query tableNames(limit: 20) { myField }` - * You can insert a row: `mutation { tableName_insert(data: {myField: "foo"}) }` - * You can update a row: `mutation ($id: UUID!) { tableName_update(id: $id, data: {myField: "bar"}) }` - * You can delete a row: `mutation ($id: UUID!) { tableName_delete(id: $id) }` - -##### Customizations - -- `@table(singular)` and `@table(plural)` can customize the singular and plural name. -- `@table(name)` can customize the Postgres table name. -- `@table(key)` can customize the primary key field name and type. - -For example, the `User` table often has a `uid` as its primary key. - -```graphql -type User @table(key: "uid") { - uid: String! - name: String -} -``` - - * You can securely lookup a row: `query { user(key: {uid_expr: "auth.uid"}) { name } } ` - * You can securely insert a row: `mutation { user_insert(data: {uid_expr: "auth.uid" name: "Fred"}) }` - * You can securely update a row: `mutation { user_update(key: {uid_expr: "auth.uid"}, data: {name: "New Name"}) }` - * You can securely delete a row: `mutation { user_delete(key: {uid_expr: "auth.uid"}) }` - -`@table` type can be configured further with: - - - Custom SQL data types for columns. See `@col`. - - Add SQL indexes. See `@index`. - - Add SQL unique constraints. See `@unique`. - - Add foreign key constraints to define relations. See `@ref`. - -""" -directive @table( - """ - Configures the SQL database table name. Defaults to snake_case like `table_name`. - """ - name: String - """ - Configures the singular name. Defaults to the camelCase like `tableName`. - """ - singular: String - """ - Configures the plural name. Defaults to infer based on English plural pattern like `tableNames`. - """ - plural: String - """ - Defines the primary key of the table. Defaults to a single field named `id`. - If not present already, Data Connect adds an implicit field `id: UUID! @default(expr: "uuidV4()")`. - """ - key: [String!] -) on OBJECT - -""" -Defines a relational database Raw SQLview. - -Data Connect generates GraphQL queries with WHERE and ORDER BY clauses. -However, not all SQL features has native GraphQL equivalent. - -You can write **an arbitrary SQL SELECT statement**. Data Connect -would map Graphql fields on `@view` type to columns in your SELECT statement. - -* Scalar GQL fields (camelCase) should match a SQL column (snake_case) - in the SQL SELECT statement. -* Reference GQL field can point to another `@table` type. Similar to foreign key - defined with `@ref` on a `@table` type, a `@view` type establishes a relation - when `@ref(fields)` match `@ref(references)` on the target table. - -In this example, you can use `@view(sql)` to define an aggregation view on existing -table. - -```graphql -type User @table { - name: String - score: Int -} -type UserAggregation @view(sql: ''' - SELECT - COUNT(*) as count, - SUM(score) as sum, - AVG(score) as average, - PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) AS median, - (SELECT id FROM "user" LIMIT 1) as example_id - FROM "user" -''') { - count: Int - sum: Int - average: Float - median: Float - example: User - exampleId: UUID -} -``` - -###### Example: Query Raw SQL View - -```graphql -query { - userAggregations { - count sum average median - exampleId example { id } - } -} -``` - -##### One-to-One View - -An one-to-one companion `@view` can be handy if you want to argument a `@table` -with additional implied content. - -```graphql -type Restaurant @table { - name: String! -} -type Review @table { - restaurant: Restaurant! - rating: Int! -} -type RestaurantStats @view(sql: ''' - SELECT - restaurant_id, - COUNT(*) AS review_count, - AVG(rating) AS average_rating - FROM review - GROUP BY restaurant_id -''') { - restaurant: Restaurant @unique - reviewCount: Int - averageRating: Float -} -``` - -In this example, `@unique` convey the assumption that each `Restaurant` should -have only one `RestaurantStats` object. - -###### Example: Query One-to-One View - -```graphql -query ListRestaurants { - restaurants { - name - stats: restaurantStats_on_restaurant { - reviewCount - averageRating - } - } -} -``` - -###### Example: Filter based on One-to-One View - -```graphql -query BestRestaurants($minAvgRating: Float, $minReviewCount: Int) { - restaurants(where: { - restaurantStats_on_restaurant: { - averageRating: {ge: $minAvgRating} - reviewCount: {ge: $minReviewCount} - } - }) { name } -} -``` - -##### Customizations - -- One of `@view(sql)` or `@view(name)` should be defined. - `@view(name)` can refer to a persisted SQL view in the Postgres schema. -- `@view(singular)` and `@view(plural)` can customize the singular and plural name. - -`@view` type can be configured further: - - - `@unique` lets you define one-to-one relation. - - `@col` lets you customize SQL column mapping. For example, `@col(name: "column_in_select")`. - -##### Limitations - -Raw SQL view doesn't have a primary key, so it doesn't support lookup. Other -`@table` or `@view` cannot have `@ref` to a view either. - -View cannot be mutated. You can perform CRUD operations on the underlying -table to alter its content. - -**Important: Data Connect doesn't parse and validate SQL** - -- If the SQL view is invalid or undefined, related requests may fail. -- If the SQL view return incompatible types. Firebase Data Connect may surface - errors. -- If a field doesn't have a corresponding column in the SQL SELECT statement, - it will always be `null`. -- There is no way to ensure VIEW to TABLE `@ref` constraint. -- All fields must be nullable in case they aren't found in the SELECT statement - or in the referenced table. - -**Important: You should always test `@view`!** - -""" -directive @view( - """ - The SQL view name. If neither `name` nor `sql` are provided, defaults to the - snake_case of the singular type name. - `name` and `sql` cannot be specified at the same time. - """ - name: String @fdc_oneOf - """ - SQL `SELECT` statement used as the basis for this type. - SQL SELECT columns should use snake_case. GraphQL fields should use camelCase. - `name` and `sql` cannot be specified at the same time. - """ - sql: String @fdc_oneOf - """ - Configures the singular name. Defaults to the camelCase like `viewName`. - """ - singular: String - """ - Configures the plural name. Defaults to infer based on English plural pattern like `viewNames`. - """ - plural: String -) on OBJECT - -""" -Customizes a field that represents a SQL database table column. - -Data Connect maps scalar Fields on `@table` type to a SQL column of -corresponding data type. - -- scalar `UUID` maps to [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). -- scalar `String` maps to [`text`](https://www.postgresql.org/docs/current/datatype-character.html). -- scalar `Int` maps to [`int`](https://www.postgresql.org/docs/current/datatype-numeric.html). -- scalar `Int64` maps to [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html). -- scalar `Float` maps to [`double precision`](https://www.postgresql.org/docs/current/datatype-numeric.html). -- scalar `Boolean` maps to [`boolean`](https://www.postgresql.org/docs/current/datatype-boolean.html). -- scalar `Date` maps to [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html). -- scalar `Timestamp` maps to [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html). -- scalar `Any` maps to [`jsonb`](https://www.postgresql.org/docs/current/datatype-json.html). -- scalar `Vector` maps to [`pgvector`](https://github.com/pgvector/pgvector). - -Array scalar fields are mapped to [Postgres arrays](https://www.postgresql.org/docs/current/arrays.html). - -###### Example: Serial Primary Key - -For example, you can define auto-increment primary key. - -```graphql -type Post @table { - id: Int! @col(name: "post_id", dataType: "serial") -} -``` - -Data Connect converts it to the following SQL table schema. - -```sql -CREATE TABLE "public"."post" ( - "post_id" serial NOT NULL, - PRIMARY KEY ("id") -) -``` - -###### Example: Vector - -```graphql -type Post @table { - content: String! @col(name: "post_content") - contentEmbedding: Vector! @col(size:768) -} -``` - -""" -directive @col( - """ - The SQL database column name. Defaults to snake_case of the field name. - """ - name: String - """ - Configures the custom SQL data type. - - Each GraphQL type can map to multiple SQL data types. - Refer to [Postgres supported data types](https://www.postgresql.org/docs/current/datatype.html). - - Incompatible SQL data type will lead to undefined behavior. - """ - dataType: String - """ - Required on `Vector` columns. It specifies the length of the Vector. - `textembedding-gecko@003` model generates `Vector` of `@col(size:768)`. - """ - size: Int -) on FIELD_DEFINITION - - -""" -Defines a foreign key reference to another table. - -For example, we can define a many-to-one relation. - -```graphql -type ManyTable @table { - refField: OneTable! -} -type OneTable @table { - someField: String! -} -``` -Data Connect adds implicit foreign key column and relation query field. So the -above schema is equivalent to the following schema. - -```graphql -type ManyTable @table { - id: UUID! @default(expr: "uuidV4()") - refField: OneTable! @ref(fields: "refFieldId", references: "id") - refFieldId: UUID! -} -type OneTable @table { - id: UUID! @default(expr: "uuidV4()") - someField: UUID! - # Generated Fields: - # manyTables_on_refField: [ManyTable!]! -} -``` -Data Connect generates the necessary foreign key constraint. - -```graphql -CREATE TABLE "public"."many_table" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "ref_field_id" uuid NOT NULL, - PRIMARY KEY ("id"), - CONSTRAINT "many_table_ref_field_id_fkey" FOREIGN KEY ("ref_field_id") REFERENCES "public"."one_table" ("id") ON DELETE CASCADE -) -``` - -###### Example: Traverse the Reference Field - -```graphql -query ($id: UUID!) { - manyTable(id: $id) { - refField { id } - } -} -``` - -###### Example: Reverse Traverse the Reference field - -```graphql -query ($id: UUID!) { - oneTable(id: $id) { - manyTables_on_refField { id } - } -} -``` - -##### Optional Many-to-One Relation - -An optional foreign key reference will be set to null if the referenced row is deleted. - -In this example, if a `User` is deleted, the `assignee` and `reporter` -references will be set to null. - -```graphql -type Bug @table { - title: String! - assignee: User - reproter: User -} - -type User @table { name: String! } -``` - -##### Required Many-to-One Relation - -A required foreign key reference will cascade delete if the referenced row is -deleted. - -In this example, if a `Post` is deleted, associated comments will also be -deleted. - -```graphql -type Comment @table { - post: Post! - content: String! -} - -type Post @table { title: String! } -``` - -##### Many To Many Relation - -You can define a many-to-many relation with a join table. - -```graphql -type Membership @table(key: ["group", "user"]) { - group: Group! - user: User! - role: String! @default(value: "member") -} - -type Group @table { name: String! } -type User @table { name: String! } -``` - -When Data Connect sees a table with two reference field as its primary key, it -knows this is a join table, so expands the many-to-many query field. - -```graphql -type Group @table { - name: String! - # Generated Fields: - # users_via_Membership: [User!]! - # memberships_on_group: [Membership!]! -} -type User @table { - name: String! - # Generated Fields: - # groups_via_Membership: [Group!]! - # memberships_on_user: [Membership!]! -} -``` - -###### Example: Traverse the Many-To-Many Relation - -```graphql -query ($id: UUID!) { - group(id: $id) { - users: users_via_Membership { - name - } - } -} -``` - -###### Example: Traverse to the Join Table - -```graphql -query ($id: UUID!) { - group(id: $id) { - memberships: memberships_on_group { - user { name } - role - } - } -} -``` - -##### One To One Relation - -You can even define a one-to-one relation with the help of `@unique` or `@table(key)`. - -```graphql -type User @table { - name: String -} -type Account @table { - user: User! @unique -} -# Alternatively, use primary key constraint. -# type Account @table(key: "user") { -# user: User! -# } -``` - -###### Example: Transerse the Reference Field - -```graphql -query ($id: UUID!) { - account(id: $id) { - user { id } - } -} -``` - -###### Example: Reverse Traverse the Reference field - -```graphql -query ($id: UUID!) { - user(id: $id) { - account_on_user { id } - } -} -``` - -##### Customizations - -- `@ref(constraintName)` can customize the SQL foreign key constraint name (`table_name_ref_field_fkey` above). -- `@ref(fields)` can customize the foreign key field names. -- `@ref(references)` can customize the constraint to reference other columns. - By default, `@ref(references)` is the primary key of the `@ref` table. - Other fields with `@unique` may also be referred in the foreign key constraint. - -""" -directive @ref( - "The SQL database foreign key constraint name. Defaults to snake_case `{table_name}_{field_name}_fkey`." - constraintName: String - """ - Foreign key fields. Defaults to `{tableName}{PrimaryIdName}`. - """ - fields: [String!] - "The fields that the foreign key references in the other table. Defaults to its primary key." - references: [String!] -) on FIELD_DEFINITION - -"Defines the orderBy direction in a query." -enum OrderDirection { -"Results are ordered in ascending order." - ASC -"Results are ordered in descending order." - DESC -} - -""" -Specifies the default value for a column field. - -For example: - -```graphql -type User @table(key: "uid") { - uid: String! @default(expr: "auth.uid") - number: Int! @col(dataType: "serial") - createdAt: Date! @default(expr: "request.time") - role: String! @default(value: "Member") - credit: Int! @default(value: 100) -} -``` - -The supported arguments vary based on the field type. -""" -directive @default( - "A constant value validated against the field's GraphQL type during compilation." - value: Any @fdc_oneOf(required: true) - "A CEL expression whose return value must match the field's data type." - expr: Any_Expr @fdc_oneOf(required: true) - """ - A raw SQL expression, whose SQL data type must match the underlying column. - - The value is any variable-free expression (in particular, cross-references to - other columns in the current table are not allowed). Subqueries are not allowed either. - See [PostgreSQL defaults](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-DEFAULT) - for more details. - """ - sql: Any_SQL @fdc_oneOf(required: true) -) on FIELD_DEFINITION - -""" -Defines a database index to optimize query performance. - -```graphql -type User @table @index(fields: ["name", "phoneNumber"], order: [ASC, DESC]) { - name: String @index - phoneNumber: Int64 @index - tags: [String] @index # GIN Index -} -``` - -##### Single Field Index - -You can put `@index` on a `@col` field to create a SQL index. - -`@index(order)` matters little for single field indexes, as they can be scanned -in both directions. - -##### Composite Index - -You can put `@index(fields: [...])` on `@table` type to define composite indexes. - -`@index(order: [...])` can customize the index order to satisfy particular -filter and order requirement. - -""" -directive @index( - """ - Configure the SQL database index id. - - If not overridden, Data Connect generates the index name: - - `{table_name}_{first_field}_{second_field}_aa_idx` - - `{table_name}_{field_name}_idx` - """ - name: String - """ - Only allowed and required when used on a `@table` type. - Specifies the fields to create the index on. - """ - fields: [String!] - """ - Only allowed for `BTREE` `@index` on `@table` type. - Specifies the order for each indexed column. Defaults to all `ASC`. - """ - order: [IndexFieldOrder!] - """ - Customize the index type. - - For most index, it defaults to `BTREE`. - For array fields, only allowed `IndexType` is `GIN`. - For `Vector` fields, defaults to `HNSW`, may configure to `IVFFLAT`. - """ - type: IndexType - """ - Only allowed when used on vector field. - Defines the vector similarity method. Defaults to `INNER_PRODUCT`. - """ - vector_method: VectorSimilarityMethod -) repeatable on FIELD_DEFINITION | OBJECT - -"Specifies the sorting order for database indexes." -enum IndexFieldOrder { - "Sorts the field in ascending order (from lowest to highest)." - ASC - "Sorts the field in descending order (from highest to lowest)." - DESC -} - -"Defines the type of index to be used in the database." -enum IndexType { - "A general-purpose index type commonly used for sorting and searching." - BTREE - "Generalized Inverted Index, optimized for indexing composite values such as arrays." - GIN - "Hierarchical Navigable Small World graph, used for nearest-neighbor searches on vector fields." - HNSW - "Inverted File Index, optimized for approximate nearest-neighbor searches in vector databases." - IVFFLAT -} - -""" -Defines unique constraints on `@table`. - -For example, - -```graphql -type User @table { - phoneNumber: Int64 @unique -} -type UserProfile @table { - user: User! @unique - address: String @unique -} -``` - -- `@unique` on a `@col` field adds a single-column unique constraint. -- `@unique` on a `@table` type adds a composite unique constraint. -- `@unique` on a `@ref` defines a one-to-one relation. It adds unique constraint - on `@ref(fields)`. - -`@unique` ensures those fields can uniquely identify a row, so other `@table` -type may define `@ref(references)` to refer to fields that has a unique constraint. - -""" -directive @unique( - """ - Configures the SQL database unique constraint name. - - If not overridden, Data Connect generates the unique constraint name: - - `table_name_first_field_second_field_uidx` - - `table_name_only_field_name_uidx` - """ - indexName: String - """ - Only allowed and required when used on OBJECT, - this specifies the fields to create a unique constraint on. - """ - fields: [String!] -) repeatable on FIELD_DEFINITION | OBJECT - -""" -Date is a string in the YYYY-MM-DD format representing a local-only date. - -See the description for Timestamp for range and limitations. - -As a FDC-specific extension, inputs that includes time portions (as specified by -the Timestamp scalar) are accepted but only the date portion is used. In other -words, only the part before "T" is used and the rest discarded. This effectively -truncates it to the local date in the specified time-zone. - -Outputs will always be in the canonical YYYY-MM-DD format. - -In the PostgreSQL table, it's stored as [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html). -""" -scalar Date @specifiedBy(url: "https://scalars.graphql.org/andimarek/local-date.html") - -""" -Timestamp is a RFC 3339 string that represents an exact point in time. - -The serialization format follows https://scalars.graphql.org/andimarek/date-time -except the "Non-optional exact milliseconds" Section. As a FDC-specific -extension, inputs and outputs may contain 0, 3, 6, or 9 fractional digits. - -Specifically, output precision varies by server-side factors such as data source -support and clients must not rely on an exact number of digits. Clients may -truncate extra digits as fit, with the caveat that there may be information loss -if the truncated value is subsequently sent back to the server. - -FDC only supports year 1583 to 9999 (inclusive) and uses the ISO-8601 calendar -system for all date-time calculations. Notably, the expanded year representation -(+/-YYYYY) is rejected and Year 1582 and before may either be rejected or cause -undefined behavior. - -In the PostgreSQL table, it's stored as [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html). -""" -scalar Timestamp @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time") - -""" -A Common Expression Language (CEL) expression that returns a Timestamp at runtime. - -Limitation: Right now, only a few expressions are supported. -""" -scalar Timestamp_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression(returnType: "google.protobuf.Timestamp") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "request.time", description: "The timestamp when the request is received (with microseconds precision).") - -""" -A Common Expression Language (CEL) expression that returns a Timestamp at runtime, -which is then truncated to UTC date only. The time-of-day parts are discarded. - -Limitation: Right now, only a few expressions are supported. -""" -scalar Date_Expr - @specifiedBy(url: "https://github.com/google/cel-spec") - @fdc_celExpression(returnType: "google.protobuf.Timestamp") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "request.time", description: "The UTC date on which the request is received.") - -"Conditions on a `Date` value." -input Date_Filter { - "Match if the field `IS NULL`." - isNull: Boolean - "Match if the field is exactly equal to the provided value." - eq: Date @fdc_oneOf(group: "eq") - "Match if the field equals the provided CEL expression." - eq_expr: Date_Expr @fdc_oneOf(group: "eq") - "Match if the field equals the provided relative date." - eq_date: Date_Relative @fdc_oneOf(group: "eq") - "Match if the field is not equal to the provided value." - ne: Date @fdc_oneOf(group: "ne") - "Match if the field is not equal to the provided CEL expression." - ne_expr: Date_Expr @fdc_oneOf(group: "ne") - "Match if the field is not equal to the provided relative date." - ne_date: Date_Relative @fdc_oneOf(group: "ne") - "Match if the field value is among the provided list of values." - in: [Date!] - "Match if the field value is not among the provided list of values." - nin: [Date!] - "Match if the field value is greater than the provided value." - gt: Date @fdc_oneOf(group: "gt") - "Match if the field value is greater than the provided CEL expression." - gt_expr: Date_Expr @fdc_oneOf(group: "gt") - "Match if the field value is greater than the provided relative date." - gt_date: Date_Relative @fdc_oneOf(group: "gt") - "Match if the field value is greater than or equal to the provided value." - ge: Date @fdc_oneOf(group: "ge") - "Match if the field value is greater than or equal to the provided CEL expression." - ge_expr: Date_Expr @fdc_oneOf(group: "ge") - "Match if the field value is greater than or equal to the provided relative date." - ge_date: Date_Relative @fdc_oneOf(group: "ge") - "Match if the field value is less than the provided value." - lt: Date @fdc_oneOf(group: "lt") - "Match if the field value is less than the provided CEL expression." - lt_expr: Date_Expr @fdc_oneOf(group: "lt") - "Match if the field value is less than the provided relative date." - lt_date: Date_Relative @fdc_oneOf(group: "lt") - "Match if the field value is less than or equal to the provided value." - le: Date @fdc_oneOf(group: "le") - "Match if the field value is less than or equal to the provided CEL expression." - le_expr: Date_Expr @fdc_oneOf(group: "le") - "Match if the field value is less than or equal to the provided relative date." - le_date: Date_Relative @fdc_oneOf(group: "le") -} - -"Conditions on a`Date` list." -input Date_ListFilter { - "Match if the list contains the provided date." - includes: Date @fdc_oneOf(group: "includes") - "Match if the list contains the provided date CEL expression." - includes_expr: Date_Expr @fdc_oneOf(group: "includes") - "Match if the list contains the provided relative date." - includes_date: Date_Relative @fdc_oneOf(group: "includes") - "Match if the list does not contain the provided date." - excludes: Date @fdc_oneOf(group: "excludes") - "Match if the list does not contain the provided date CEL expression." - excludes_expr: Date_Expr @fdc_oneOf(group: "excludes") - "Match if the list does not contain the provided relative date." - excludes_date: Date_Relative @fdc_oneOf(group: "excludes") - "Match if the list contains all the provided dates." - includesAll: [Date!] - "Match if the list contains none of the provided dates." - excludesAll: [Date!] -} - -"Conditions on a `Timestamp` value." -input Timestamp_Filter { - "Match if the field `IS NULL`." - isNull: Boolean - "Match if the field is exactly equal to the provided value." - eq: Timestamp @fdc_oneOf(group: "eq") - "Match if the field equals the provided CEL expression." - eq_expr: Timestamp_Expr @fdc_oneOf(group: "eq") - "Match if the field equals the provided relative time." - eq_time: Timestamp_Relative @fdc_oneOf(group: "eq") - "Match if the field is not equal to the provided value." - ne: Timestamp @fdc_oneOf(group: "ne") - "Match if the field is not equal to the provided CEL expression." - ne_expr: Timestamp_Expr @fdc_oneOf(group: "ne") - "Match if the field is not equal to the provided relative time." - ne_time: Timestamp_Relative @fdc_oneOf(group: "ne") - "Match if the field value is among the provided list of values." - in: [Timestamp!] - "Match if the field value is not among the provided list of values." - nin: [Timestamp!] - "Match if the field value is greater than the provided value." - gt: Timestamp @fdc_oneOf(group: "gt") - "Match if the field value is greater than the provided CEL expression." - gt_expr: Timestamp_Expr @fdc_oneOf(group: "gt") - "Match if the field value is greater than the provided relative time." - gt_time: Timestamp_Relative @fdc_oneOf(group: "gt") - "Match if the field value is greater than or equal to the provided value." - ge: Timestamp @fdc_oneOf(group: "ge") - "Match if the field value is greater than or equal to the provided CEL expression." - ge_expr: Timestamp_Expr @fdc_oneOf(group: "ge") - "Match if the field value is greater than or equal to the provided relative time." - ge_time: Timestamp_Relative @fdc_oneOf(group: "ge") - "Match if the field value is less than the provided value." - lt: Timestamp @fdc_oneOf(group: "lt") - "Match if the field value is less than the provided CEL expression." - lt_expr: Timestamp_Expr @fdc_oneOf(group: "lt") - "Match if the field value is less than the provided relative time." - lt_time: Timestamp_Relative @fdc_oneOf(group: "lt") - "Match if the field value is less than or equal to the provided value." - le: Timestamp @fdc_oneOf(group: "le") - "Match if the field value is less than or equal to the provided CEL expression." - le_expr: Timestamp_Expr @fdc_oneOf(group: "le") - "Match if the field value is less than or equal to the provided relative time." - le_time: Timestamp_Relative @fdc_oneOf(group: "le") -} - -"Conditions on a `Timestamp` list." -input Timestamp_ListFilter { - "Match if the list contains the provided timestamp." - includes: Timestamp @fdc_oneOf(group: "includes") - "Match if the list contains the provided timestamp CEL expression." - includes_expr: Timestamp_Expr @fdc_oneOf(group: "includes") - "Match if the list contains the provided relative timestamp." - includes_time: Timestamp_Relative @fdc_oneOf(group: "includes") - "Match if the list does not contain the provided timestamp." - excludes: Timestamp @fdc_oneOf(group: "excludes") - "Match if the list does not contain the provided timestamp CEL expression." - excludes_expr: Timestamp_Expr @fdc_oneOf(group: "excludes") - "Match if the list does not contain the provided relative timestamp." - excludes_time: Timestamp_Relative @fdc_oneOf(group: "excludes") - "Match if the list contains all the provided timestamps." - includesAll: [Timestamp!] - "Match if the list contains none of the provided timestamps." - excludesAll: [Timestamp!] -} - -"Update input of a `Date` value." -input Date_Update { - "Set the field to the provided date." - set: Date @fdc_oneOf(group: "set") - "Set the field to the provided date CEL expression." - set_expr: Date_Expr @fdc_oneOf(group: "set") - "Set the field to the provided relative date." - set_date: Date_Relative @fdc_oneOf(group: "set") -} - -"Update input of a `Date` list value." -input Date_ListUpdate { - "Replace the current list with the provided list of `Date` values." - set: [Date!] - "Append the provided `Date` values to the existing list." - append: [Date!] - "Prepend the provided `Date` values to the existing list." - prepend: [Date!] - "Remove the date value at the specified index." - delete: Int - "The index of the list to perform updates." - i: Int - "Update the date value at the specified index." - update: Date -} - -"Update input of a `Timestamp` value." -input Timestamp_Update { - "Set the field to the provided timestamp." - set: Timestamp @fdc_oneOf(group: "set") - "Set the field to the provided timestamp CEL expression." - set_expr: Timestamp_Expr @fdc_oneOf(group: "set") - "Set the field to the provided relative timestamp." - set_time: Timestamp_Relative @fdc_oneOf(group: "set") -} - -"Update input of an `Timestamp` list value." -input Timestamp_ListUpdate { - "Replace the current list with the provided list of `Timestamp` values." - set: [Timestamp!] - "Append the provided `Timestamp` values to the existing list." - append: [Timestamp!] - "Prepend the provided `Timestamp` values to the existing list." - prepend: [Timestamp!] - "Remove the timestamp value at the specified index." - delete: Int - "The index of the list to perform updates." - i: Int - "Update the timestamp value at the specified index." - update: Timestamp -} - - -"A runtime-calculated `Timestamp` value relative to `now` or `at`." -input Timestamp_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { - "Match for the current time." - now: True @fdc_oneOf(group: "from", required: true) - "A specific timestamp for matching." - at: Timestamp @fdc_oneOf(group: "from", required: true) - "Add the provided duration to the base timestamp." - add: Timestamp_Duration - "Subtract the provided duration from the base timestamp." - sub: Timestamp_Duration - "Truncate the timestamp to the provided interval." - truncateTo: Timestamp_Interval -} - -input Timestamp_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { - "The number of milliseconds for the duration." - milliseconds: Int! = 0 - "The number of seconds for the duration." - seconds: Int! = 0 - "The number of minutes for the duration." - minutes: Int! = 0 - "The number of hours for the duration." - hours: Int! = 0 - "The number of days for the duration." - days: Int! = 0 - "The number of weeks for the duration." - weeks: Int! = 0 - "The number of months for the duration." - months: Int! = 0 - "The number of years for the duration." - years: Int! = 0 -} - -enum Timestamp_Interval @fdc_forbiddenAsFieldType { - "Represents a time interval of one second." - SECOND - "Represents a time interval of one minute." - MINUTE - "Represents a time interval of one hour." - HOUR - "Represents a time interval of one day." - DAY - "Represents a time interval of one week." - WEEK - "Represents a time interval of one month." - MONTH - "Represents a time interval of one year." - YEAR -} - -"A runtime-calculated Date value relative to `today` or `on`." -input Date_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { - "Match for today’s date." - today: True @fdc_oneOf(group: "from", required: true) - "A specific date for matching." - on: Date @fdc_oneOf(group: "from", required: true) - "Add the provided duration to the base date." - add: Date_Duration - "Subtract the provided duration from the base date." - sub: Date_Duration - "Truncate the date to the provided interval." - truncateTo: Date_Interval -} - -input Date_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { - "The number of days for the duration." - days: Int! = 0 - "The number of weeks for the duration." - weeks: Int! = 0 - "The number of months for the duration." - months: Int! = 0 - "The number of years for the duration." - years: Int! = 0 -} - -enum Date_Interval @fdc_forbiddenAsFieldType { - "Represents a time interval of one week." - WEEK - "Represents a time interval of one month." - MONTH - "Represents a time interval of one year." - YEAR -} - -"Update input of a `String` value." -input String_Update { - "Set the field to a provided value." - set: String @fdc_oneOf(group: "set") - "Set the field to a provided server value expression." - set_expr: String_Expr @fdc_oneOf(group: "set") -} - -"Update input of a `String` list value." -input String_ListUpdate { - "Set the list with the provided values." - set: [String!] - "Append the provided values to the existing list." - append: [String!] - "Prepend the provided values to the existing list." - prepend: [String!] -} - -"Update input of a `UUID` value." -input UUID_Update { - "Set the field to a provided UUID." - set: UUID @fdc_oneOf(group: "set") - "Set the field to a provided UUID expression." - set_expr: UUID_Expr @fdc_oneOf(group: "set") -} - -"Update input of an `ID` list value." -input UUID_ListUpdate { - "Set the list with the provided list of UUIDs." - set: [UUID!] - "Append the provided UUIDs to the existing list." - append: [UUID!] - "Prepend the provided UUIDs to the existing list." - prepend: [UUID!] -} - -"Update input of an `Int` value." -input Int_Update { - "Set the field to a provided value." - set: Int - "Increment the field by a provided value." - inc: Int - "Decrement the field by a provided value." - dec: Int -} - -"Update input of an `Int` list value." -input Int_ListUpdate { - "Set the list with the provided values." - set: [Int!] - "Append the provided list of values to the existing list." - append: [Int!] - "Prepend the provided list of values to the existing list." - prepend: [Int!] -} - -"Update input of an `Int64` value." -input Int64_Update { - "Set the field to a provided value." - set: Int64 - "Increment the field by a provided value." - inc: Int64 - "Decrement the field by a provided value." - dec: Int64 -} - -"Update input of an `Int64` list value." -input Int64_ListUpdate { - "Replace the list with the provided values." - set: [Int64!] - "Append the provided list of values to the existing list." - append: [Int64!] - "Prepend the provided list of values to the existing list." - prepend: [Int64!] -} - -"Update input of a `Float` value." -input Float_Update { - "Set the field to a provided value." - set: Float - "Increment the field by a provided value." - inc: Float - "Decrement the field by a provided value." - dec: Float -} - -"Update input of a `Float` list value." -input Float_ListUpdate { - "Set the list with the provided values." - set: [Float!] - "Append the provided list of values to the existing list." - append: [Float!] - "Prepend the provided list of values to the existing list." - prepend: [Float!] -} - -"Update input of a `Boolean` value." -input Boolean_Update { - "Set the field to a provided value." - set: Boolean -} - -"Update input of a `Boolean` list value." -input Boolean_ListUpdate { - "Set the list with the provided values." - set: [Boolean!] - "Append the provided list of values to the existing list." - append: [Boolean!] - "Prepend the provided list of values to the existing list." - prepend: [Boolean!] -} - -"Update input of an `Any` value." -input Any_Update { - "Set the field to a provided value." - set: Any -} - -"Update input of an `Any` list value." -input Any_ListUpdate { - "Set the list with the provided values." - set: [Any!] - "Append the provided list of values to the existing list." - append: [Any!] - "Prepend the provided list of values to the existing list." - prepend: [Any!] -} - -type Query { - """ - _service provides customized introspection on Firebase Data Connect Sevice. - """ - _service: _Service! -} - -""" -Vector is an array of single-precision floating-point numbers, serialized -as a JSON array. All elements must be finite (no NaN, Infinity or -Infinity). - -Example: [1.1, 2, 3.3] - -In the PostgreSQL table, it's stored as [`pgvector`](https://github.com/pgvector/pgvector). - -See `Vector_Embed` for how to generate text embeddings in query and mutations. -""" -scalar Vector - -""" -Defines the similarity function to use when comparing vectors in queries. - -Defaults to `INNER_PRODUCT`. - -View [all vector functions](https://github.com/pgvector/pgvector?tab=readme-ov-file#vector-functions). -""" -enum VectorSimilarityMethod { - "Measures the Euclidean (L2) distance between two vectors." - L2 - "Measures the cosine similarity between two vectors." - COSINE - "Measures the inner product(dot product) between two vectors." - INNER_PRODUCT -} - -"Conditions on a Vector value." -input Vector_Filter { - "Match if the field is exactly equal to the provided vector." - eq: Vector - "Match if the field is not equal to the provided vector." - ne: Vector - "Match if the field value is among the provided list of vectors." - in: [Vector!] - "Match if the field value is not among the provided list of vectors." - nin: [Vector!] - "Match if the field is `NULL`." - isNull: Boolean -} - -input Vector_ListFilter { - "Match if the list includes the supplied vector." - includes: Vector - "Match if the list does not include the supplied vector." - excludes: Vector - "Match if the list contains all the provided vectors." - includesAll: [Vector!] - "Match if the list contains none of the provided vectors." - excludesAll: [Vector!] -} - -"Update input of a Vector value." -input Vector_Update { - "Set the field to the provided vector value." - set: Vector @fdc_oneOf(group: "set") - "Set the field to the vector embedding result from a text input." - set_embed: Vector_Embed @fdc_oneOf(group: "set") -} - - -"Update input of a Vector list value." -input Vector_ListUpdate { - "Replace the current list with the provided list of Vector values." - set: [Vector] - "Append the provided Vector values to the existing list." - append: [Vector] - "Prepend the provided Vector values to the existing list." - prepend: [Vector] - "Delete the vector at the specified index." - delete: Int - "The index of the vector to be updated." - i: Int - "Update the vector at the specified index." - update: Vector -} - -""" -Create a vector embedding of text using the given model on Vertex AI. - -Cloud SQL for Postgresql natively integrates with [Vertex AI Text embeddings API](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api) -to effectively generate text embeddings. - -If you uses [`Vector`](scalar.md#Vector) in your schema, Firebase Data Connect automatically installs -[`pgvector`](https://github.com/pgvector/pgvector) and [`google_ml_integration`](https://cloud.google.com/sql/docs/postgres/integrate-cloud-sql-with-vertex-ai) -Postgres extensions in your Cloud SQL database. - -Given a Post table with a `Vector` embedding field. - -```graphql -type Post @table { - content: String! - contentEmbedding: Vector @col(size:768) -} -``` - -NOTE: All natively supported `Vector_Embed_Model` generates vector of length `768`. - -###### Example: Insert embedding - -```graphql -mutation CreatePost($content: String!) { - post_insert(data: { - content: $content, - contentEmbedding_embed: {model: "textembedding-gecko@003", text: $content}, - }) -} -``` - -###### Example: Vector similarity Search - -```graphql -query SearchPost($query: String!) { - posts_contentEmbedding_similarity(compare_embed: {model: "textembedding-gecko@003", text: $query}) { - id - content - } -} -``` -""" -input Vector_Embed @fdc_forbiddenAsVariableType { - """ - The model to use for vector embedding. - Recommend the latest stable model: `textembedding-gecko@003`. - """ - model: Vector_Embed_Model! - "The text to generate the vector embedding from." - text: String! -} - -""" -The Vertex AI model version that is required in input `Vector_Embed`. - -It is recommended to use the latest stable model version: `textembedding-gecko@003`. - -View all supported [Vertex AI Text embeddings APIs](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api). -""" -scalar Vector_Embed_Model - @specifiedBy(url: "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning") - @fdc_forbiddenAsVariableType - @fdc_forbiddenAsFieldType - @fdc_example(value: "textembedding-gecko@003", description: "A stable version of the textembedding-gecko model") - @fdc_example(value: "textembedding-gecko@001", description: "An older version of the textembedding-gecko model") - @fdc_example(value: "text-embedding-004", description: "Another text embedding model") - -""" -Redact a part of the response from the client. - -Redacted fields are still evaluated for side effects (including data changes and -`@check`) and the results are still available to later steps in CEL expressions -(via `response.fieldName`). -""" -directive @redact on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -""" -Ensure this field is present and is not null or `[]`, or abort the request / transaction. - -A CEL expression, `expr` is used to test the field value. It defaults to -rejecting null and `[]` but a custom expression can be provided instead. - -If the field occurs multiple times (i.e. directly or indirectly nested under a -list), `expr` will be executed once for each occurrence and `@check` succeeds if -all values succeed. `@check` fails when the field is not present at all (i.e. -all ancestor paths contain `null` or `[]`), unless `optional` is true. - -If a `@check` fails in a mutation, the top-level field containing it will be -replaced with a partial error, whose message can be customzied via the `message` -argument. Each subsequent top-level fields will return an aborted error (i.e. -not executed). To rollback previous steps, see `@transaction`. -""" -directive @check( - """ - The CEL expression to test the field value (or values if nested under a list). - - Within the CEL expression, a special value `this` evaluates to the field that - this directive is attached to. If this field occurs multiple times because - any ancestor is a list, each occurrence is tested with `this` bound to each - value. When the field itself is a list or object, `this` follows the same - structure (including all decendants selected in case of objects). - - For any given path, if an ancestor is `null` or `[]`, the field will not be - reached and the CEL evaluation will be skipped for that path. In other words, - evaluation only takes place when `this` is `null` or non-null, but never - undefined. (See also the `optional` argument.) - """ - expr: Boolean_Expr! = "!(this in [null, []])" - """ - The error message to return to the client if the check fails. - - Defaults to "permission denied" if not specified. - """ - message: String! = "permission denied" - """ - Whether the check should pass or fail (default) when the field is not present. - - A field will not be reached at a given path if its parent or any ancestor is - `[]` or `null`. When this happens to all paths, the field will not be present - anywhere in the response tree. In other words, `expr` is evaluated 0 times. - By default, @check will automatically fail in this case. Set this argument to - `true` to make it pass even if no tests are run (a.k.a. "vacuously true"). - """ - optional: Boolean = false -) repeatable on QUERY | MUTATION | FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT - -type Mutation { - """ - Run a query during the mutation and add fields into the response. - - Example: foo: query { users { id } } will add a field foo: {users: [{id: "..."}, …]} into the response JSON. - - Note: Data fetched this way can be handy for permission checks. See @check. - """ - query: Query -} - diff --git a/dataconnect/connector/connector.yaml b/dataconnect/connector/connector.yaml deleted file mode 100644 index 3e518c87..00000000 --- a/dataconnect/connector/connector.yaml +++ /dev/null @@ -1,6 +0,0 @@ -connectorId: default -generate: - javascriptSdk: - outputDir: ../../dataconnect-sdk/js/default-connector - package: "@dataconnect/default-connector" - packageJsonDir: ../.. diff --git a/dataconnect/connector/mutations.gql b/dataconnect/connector/mutations.gql deleted file mode 100644 index 6ccbe32d..00000000 --- a/dataconnect/connector/mutations.gql +++ /dev/null @@ -1,52 +0,0 @@ -# # Example mutations for a simple movie app - -# Create a movie based on user input -mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!) -@auth(level: PUBLIC) { - movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl }) -} - -# Upsert a movie -mutation UpsertMovie($id: UUID!, $title: String!, $imageUrl: String!) -@auth(level: PUBLIC) { - movie_upsert(data: { id: $id, title: $title, imageUrl: $imageUrl }) -} - -# Delete a movie -mutation DeleteMovie($id: UUID!) @auth(level: PUBLIC) { - movie_delete(id: $id) -} - -# # Upsert (update or insert) a user's username based on their auth.uid -# mutation UpsertUser($username: String!) @auth(level: USER) { -# user_upsert( -# data: { -# id_expr: "auth.uid" -# username: $username -# } -# ) -# } - -# # Add a review for a movie -# mutation AddReview( -# $movieId: UUID! -# $rating: Int! -# $reviewText: String! -# ) @auth(level: USER) { -# review_upsert( -# data: { -# userId_expr: "auth.uid" -# movieId: $movieId -# rating: $rating -# reviewText: $reviewText -# # reviewDate defaults to today in the schema. No need to set it manually. -# } -# ) -# } - -# # Logged in user can delete their review for a movie -# mutation DeleteReview( -# $movieId: UUID! -# ) @auth(level: USER) { -# review_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) -# } diff --git a/dataconnect/connector/queries.gql b/dataconnect/connector/queries.gql deleted file mode 100644 index 24e8c94c..00000000 --- a/dataconnect/connector/queries.gql +++ /dev/null @@ -1,68 +0,0 @@ -# # Example queries for a simple movie app. - -# @auth() directives control who can call each operation. -# Anyone should be able to list all movies, so the auth level is set to PUBLIC -query ListMovies @auth(level: PUBLIC) { - movies { - id - title - imageUrl - genre - } -} - -# Get movie by id -query GetMovieById($id: UUID!) @auth(level: PUBLIC) { - movie(id: $id) { - id - title - imageUrl - genre - } -} - -# # List all users, only admins should be able to list all users, so we use NO_ACCESS -# query ListUsers @auth(level: NO_ACCESS) { -# users { id, username } -# } - -# # Logged in user can list all their reviews and movie titles associated with the review -# # Since the query requires the uid of the current authenticated user, the auth level is set to USER -# query ListUserReviews @auth(level: USER) { -# user(key: {id_expr: "auth.uid"}) { -# id -# username -# # _on_ makes it easy to grab info from another table -# # Here, we use it to grab all the reviews written by the user. -# reviews: reviews_on_user { -# id -# rating -# reviewDate -# reviewText -# movie { -# id -# title -# } -# } -# } -# } - -# # Search for movies, actors, and reviews -# query SearchMovie( -# $titleInput: String -# $genre: String -# ) @auth(level: PUBLIC) { -# movies( -# where: { -# _and: [ -# { genre: { eq: $genre } } -# { title: { contains: $titleInput } } -# ] -# } -# ) { -# id -# title -# genre -# imageUrl -# } -# } diff --git a/dataconnect/dataconnect.yaml b/dataconnect/dataconnect.yaml deleted file mode 100644 index 074f3f5d..00000000 --- a/dataconnect/dataconnect.yaml +++ /dev/null @@ -1,12 +0,0 @@ -specVersion: "v1beta" -serviceId: "tanstack-query-firebase" -location: "us-central1" -schema: - source: "./schema" - datasource: - postgresql: - database: "fdcdb" - cloudSql: - instanceId: "tanstack-query-firebase-fdc" - # schemaValidation: "COMPATIBLE" -connectorDirs: ["./connector"] diff --git a/dataconnect/schema/schema.gql b/dataconnect/schema/schema.gql deleted file mode 100644 index 3a7b73ce..00000000 --- a/dataconnect/schema/schema.gql +++ /dev/null @@ -1,45 +0,0 @@ -# # Example schema for simple movie review app - -# # Users -# # Suppose a user can leave reviews for movies -# # user -> reviews is a one to many relationship, -# # movie -> reviews is a one to many relationship -# # movie <-> user is a many to many relationship -# type User @table { -# id: String! @col(name: "user_auth") -# username: String! @col(name: "username", dataType: "varchar(50)") -# # The following are generated by the user: User! field in the Review table -# # reviews_on_user -# # movies_via_Review -# } - -# Movies -type Movie @table { - # The below parameter values are generated by default with @table, and can be edited manually. - # implies directive `@col(name: "movie_id")`, generating a column name - id: UUID! @default(expr: "uuidV4()") - title: String! - imageUrl: String! - genre: String -} - -# Movie Metadata -# Movie - MovieMetadata is a one-to-one relationship -type MovieMetadata @table { - # @unique indicates a 1-1 relationship - movie: Movie! @unique - # movieId: UUID <- this is created by the above reference - rating: Float - releaseYear: Int - description: String -} - -# # Reviews -# type Review @table(name: "Reviews", key: ["movie", "user"]) { -# id: UUID! @default(expr: "uuidV4()") -# user: User! -# movie: Movie! -# rating: Int -# reviewText: String -# reviewDate: Date! @default(expr: "request.time") -# } diff --git a/firebase.json b/firebase.json deleted file mode 100644 index 93a77ec3..00000000 --- a/firebase.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "emulators": { - "firestore": { - "port": 8080 - }, - "auth": { - "port": 9099 - }, - "functions": { - "port": 5001 - }, - "database": { - "port": 9000 - }, - "ui": { - "enabled": true - } - }, - "functions": { - "predeploy": "npm --prefix \"$RESOURCE_DIR\" run build", - "source": "functions" - }, - "firestore": { - "rules": "./firestore.rules" - }, - "dataconnect": { - "source": "dataconnect" - } -} diff --git a/packages/angular/.gitignore b/packages/angular/.gitignore new file mode 100644 index 00000000..fe2c13d9 --- /dev/null +++ b/packages/angular/.gitignore @@ -0,0 +1,2 @@ +data-connect/* +index.js \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/README.md b/packages/angular/dataconnect-sdk/js/default-connector/README.md new file mode 100644 index 00000000..6ad5971f --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/README.md @@ -0,0 +1,531 @@ +# Generated TypeScript README +This README will guide you through the process of using the generated TypeScript SDK package for the connector `default`. It will also provide examples on how to use your generated SDK to call your Data Connect queries and mutations. + +***NOTE:** This README is generated alongside the generated SDK. If you make changes to this file, they will be overwritten when the SDK is regenerated.* + +You can use this generated SDK by importing from the package `@dataconnect/default-connector` as shown below. Both CommonJS and ESM imports are supported. +You can also follow the instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#set-client). + +# Accessing the connector +A connector is a collection of queries and mutations. One SDK is generated for each connector - this SDK is generated for the connector `default`. + +You can find more information about connectors in the [Data Connect documentation](https://firebase.google.com/docs/data-connect#how-does). + +In order to call Data Connect queries and mutations, you need to create an instance of the connector in your application code. + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@dataconnect/default-connector'; + +const connector: DataConnect = getDataConnect(connectorConfig); +``` + +## Connecting to the local Emulator +By default, the connector will connect to the production service. + +To connect to the emulator, you can use the following code. +You can also follow the emulator instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#instrument-clients). + +```javascript +// add connectDataConnectEmulator to your imports +import { connectDataConnectEmulator, getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@dataconnect/default-connector'; + +const connector: DataConnect = getDataConnect(connectorConfig); +connectDataConnectEmulator(connector, 'localhost', 9399); +``` + +After it's initialized, you can call your Data Connect [queries](#queries) and [mutations](#mutations) from your generated SDK. + +# Queries +There are two ways to execute a Data Connect Query using the generated Web SDK: +- Using a Query Reference function, which returns a `QueryRef` + - The `QueryRef` can be used as an argument to `executeQuery()`, which will execute the Query and return a `QueryPromise` +- Using an action shortcut function, which returns a `QueryPromise` + - Calling the action shortcut function will execute the Query and return a `QueryPromise` + +The following is true for both the action shortcut function and the `QueryRef` function: +- The `QueryPromise` returned will resolve to the result of the Query once it has finished executing +- If the Query accepts arguments, both the action shortcut function and the `QueryRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Query +- Both functions can be called with or without passing in a `DataConnect` instance as an argument + +Below are examples of how to use the `default` connector's generated functions to execute each query. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-queries). + +## ListMovies +You can execute the `ListMovies` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +listMovies(): QueryPromise; + +listMoviesRef(): QueryRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. +```javascript +listMovies(dc: DataConnect): QueryPromise; + +listMoviesRef(dc: DataConnect): QueryRef; +``` + +### Variables +The `ListMovies` query has no variables. +### Return Type +Recall that executing the `ListMovies` query returns a `QueryPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `ListMoviesData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface ListMoviesData { + movies: ({ + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key)[]; +} +``` +### Using `ListMovies`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, listMovies } from '@dataconnect/default-connector'; + +// Call the `listMovies()` function to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await listMovies(); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await listMovies(connector); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +listMovies().then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +### Using `ListMovies`'s `QueryRef` function + +```javascript +import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { connectorConfig, listMoviesRef } from '@dataconnect/default-connector'; + +// Call the `listMoviesRef()` function to get a reference to the query. +const ref = listMoviesRef(); + +// You can also pass in a `DataConnect` instance to the `QueryRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = listMoviesRef(connector); + +// Call `executeQuery()` on the reference to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeQuery(ref); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +executeQuery(ref).then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +## GetMovieById +You can execute the `GetMovieById` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +getMovieById(vars: GetMovieByIdVariables): QueryPromise; + +getMovieByIdRef(vars: GetMovieByIdVariables): QueryRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. +```javascript +getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; + +getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; +``` + +### Variables +The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface GetMovieByIdVariables { + id: UUIDString; +} +``` +### Return Type +Recall that executing the `GetMovieById` query returns a `QueryPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `GetMovieByIdData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface GetMovieByIdData { + movie?: { + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key; +} +``` +### Using `GetMovieById`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, getMovieById, GetMovieByIdVariables } from '@dataconnect/default-connector'; +// The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: +const getMovieByIdVars: GetMovieByIdVariables = { + id: ..., +} + +// Call the `getMovieById()` function to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await getMovieById(getMovieByIdVars); +// Variables can be defined inline as well. +const { data } = await getMovieById({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await getMovieById(connector, getMovieByIdVars); + +console.log(data.movie); + +// Or, you can use the `Promise` API. +getMovieById(getMovieByIdVars).then((response) => { + const data = response.data; + console.log(data.movie); +}); +``` + +### Using `GetMovieById`'s `QueryRef` function + +```javascript +import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { connectorConfig, getMovieByIdRef, GetMovieByIdVariables } from '@dataconnect/default-connector'; +// The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: +const getMovieByIdVars: GetMovieByIdVariables = { + id: ..., +} + +// Call the `getMovieByIdRef()` function to get a reference to the query. +const ref = getMovieByIdRef(getMovieByIdVars); +// Variables can be defined inline as well. +const ref = getMovieByIdRef({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the `QueryRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = getMovieByIdRef(connector, getMovieByIdVars); + +// Call `executeQuery()` on the reference to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeQuery(ref); + +console.log(data.movie); + +// Or, you can use the `Promise` API. +executeQuery(ref).then((response) => { + const data = response.data; + console.log(data.movie); +}); +``` + +# Mutations +There are two ways to execute a Data Connect Mutation using the generated Web SDK: +- Using a Mutation Reference function, which returns a `MutationRef` + - The `MutationRef` can be used as an argument to `executeMutation()`, which will execute the Mutation and return a `MutationPromise` +- Using an action shortcut function, which returns a `MutationPromise` + - Calling the action shortcut function will execute the Mutation and return a `MutationPromise` + +The following is true for both the action shortcut function and the `MutationRef` function: +- The `MutationPromise` returned will resolve to the result of the Mutation once it has finished executing +- If the Mutation accepts arguments, both the action shortcut function and the `MutationRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Mutation +- Both functions can be called with or without passing in a `DataConnect` instance as an argument + +Below are examples of how to use the `default` connector's generated functions to execute each mutation. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-mutations). + +## CreateMovie +You can execute the `CreateMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +createMovie(vars: CreateMovieVariables): MutationPromise; + +createMovieRef(vars: CreateMovieVariables): MutationRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; + +createMovieRef(dc: DataConnect, vars: CreateMovieVariables): MutationRef; +``` + +### Variables +The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface CreateMovieVariables { + title: string; + genre: string; + imageUrl: string; +} +``` +### Return Type +Recall that executing the `CreateMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `CreateMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface CreateMovieData { + movie_insert: Movie_Key; +} +``` +### Using `CreateMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, createMovie, CreateMovieVariables } from '@dataconnect/default-connector'; +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +} + +// Call the `createMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await createMovie(createMovieVars); +// Variables can be defined inline as well. +const { data } = await createMovie({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await createMovie(connector, createMovieVars); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +createMovie(createMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + +### Using `CreateMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, createMovieRef, CreateMovieVariables } from '@dataconnect/default-connector'; +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +} + +// Call the `createMovieRef()` function to get a reference to the mutation. +const ref = createMovieRef(createMovieVars); +// Variables can be defined inline as well. +const ref = createMovieRef({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = createMovieRef(connector, createMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + +## UpsertMovie +You can execute the `UpsertMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +upsertMovie(vars: UpsertMovieVariables): MutationPromise; + +upsertMovieRef(vars: UpsertMovieVariables): MutationRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; + +upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): MutationRef; +``` + +### Variables +The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface UpsertMovieVariables { + id: UUIDString; + title: string; + imageUrl: string; +} +``` +### Return Type +Recall that executing the `UpsertMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `UpsertMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface UpsertMovieData { + movie_upsert: Movie_Key; +} +``` +### Using `UpsertMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, upsertMovie, UpsertMovieVariables } from '@dataconnect/default-connector'; +// The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: +const upsertMovieVars: UpsertMovieVariables = { + id: ..., + title: ..., + imageUrl: ..., +} + +// Call the `upsertMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await upsertMovie(upsertMovieVars); +// Variables can be defined inline as well. +const { data } = await upsertMovie({ id: ..., title: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await upsertMovie(connector, upsertMovieVars); + +console.log(data.movie_upsert); + +// Or, you can use the `Promise` API. +upsertMovie(upsertMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_upsert); +}); +``` + +### Using `UpsertMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, upsertMovieRef, UpsertMovieVariables } from '@dataconnect/default-connector'; +// The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: +const upsertMovieVars: UpsertMovieVariables = { + id: ..., + title: ..., + imageUrl: ..., +} + +// Call the `upsertMovieRef()` function to get a reference to the mutation. +const ref = upsertMovieRef(upsertMovieVars); +// Variables can be defined inline as well. +const ref = upsertMovieRef({ id: ..., title: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = upsertMovieRef(connector, upsertMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_upsert); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_upsert); +}); +``` + +## DeleteMovie +You can execute the `DeleteMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +deleteMovie(vars: DeleteMovieVariables): MutationPromise; + +deleteMovieRef(vars: DeleteMovieVariables): MutationRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; + +deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): MutationRef; +``` + +### Variables +The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface DeleteMovieVariables { + id: UUIDString; +} +``` +### Return Type +Recall that executing the `DeleteMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `DeleteMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface DeleteMovieData { + movie_delete?: Movie_Key | null; +} +``` +### Using `DeleteMovie`'s action shortcut function + +```javascript +import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectorConfig, deleteMovie, DeleteMovieVariables } from '@dataconnect/default-connector'; +// The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: +const deleteMovieVars: DeleteMovieVariables = { + id: ..., +} + +// Call the `deleteMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await deleteMovie(deleteMovieVars); +// Variables can be defined inline as well. +const { data } = await deleteMovie({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const connector: DataConnect = getDataConnect(connectorConfig); +const { data } = await deleteMovie(connector, deleteMovieVars); + +console.log(data.movie_delete); + +// Or, you can use the `Promise` API. +deleteMovie(deleteMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_delete); +}); +``` + +### Using `DeleteMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, deleteMovieRef, DeleteMovieVariables } from '@dataconnect/default-connector'; +// The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: +const deleteMovieVars: DeleteMovieVariables = { + id: ..., +} + +// Call the `deleteMovieRef()` function to get a reference to the mutation. +const ref = deleteMovieRef(deleteMovieVars); +// Variables can be defined inline as well. +const ref = deleteMovieRef({ id: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const connector: DataConnect = getDataConnect(connectorConfig); +const ref = deleteMovieRef(connector, deleteMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_delete); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_delete); +}); +``` + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/index.esm.js b/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/index.esm.js new file mode 100644 index 00000000..8652740e --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/index.esm.js @@ -0,0 +1,113 @@ +import { inject } from '@angular/core'; +import { queryRef, executeQuery, mutationRef, executeMutation, validateArgs, DataConnect } from '@angular/fire/data-connect'; +import { injectDataConnectQuery, injectDataConnectMutation } from '../../../../../'; + +export const connectorConfig = { + connector: 'default', + service: 'tanstack-query-firebase', + location: 'us-central1' +}; +export function listMoviesRef(dc) { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'ListMovies'); +} +export function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +} + +export function injectListMovies(options) { + const dc = inject(DataConnect); + + return injectDataConnectQuery(() => { + const addOpn = options && options(); + return { + queryFn: () => ListMoviesRef(dc), + ...addOpn + }; + }); +} + +export function getMovieByIdRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +export function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +} + +export function injectGetMovieById(args, options) { + const dc = inject(DataConnect); + + const varsFactoryFn = (typeof args === 'function') ? args : () => args; + + return injectDataConnectQuery(() => { + const addOpn = options && options(); + return { + queryFn: () => GetMovieByIdRef(dc, varsFactoryFn()), + ...addOpn + }; + }); +} + +export function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'CreateMovie', inputVars); +} +export function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +} + +export function injectCreateMovie(args) { + return injectDataConnectMutation(CreateMovieRef, args); +} + +export function upsertMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'UpsertMovie', inputVars); +} +export function upsertMovie(dcOrVars, vars) { + return executeMutation(upsertMovieRef(dcOrVars, vars)); +} + +export function injectUpsertMovie(args) { + return injectDataConnectMutation(UpsertMovieRef, args); +} + +export function deleteMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'DeleteMovie', inputVars); +} +export function deleteMovie(dcOrVars, vars) { + return executeMutation(deleteMovieRef(dcOrVars, vars)); +} + +export function injectDeleteMovie(args) { + return injectDataConnectMutation(DeleteMovieRef, args); +} + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/package.json b/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/package.json new file mode 100644 index 00000000..7c34deb5 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/angular/esm/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/angular/index.cjs.js b/packages/angular/dataconnect-sdk/js/default-connector/angular/index.cjs.js new file mode 100644 index 00000000..d10a6057 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/angular/index.cjs.js @@ -0,0 +1,146 @@ +const { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } = require('@angular/fire/data-connect'); +const { inject } = require('@angular/core'); + +const genSdkImports = require('../'); + + +const connectorConfig = { + connector: 'default', + service: 'tanstack-query-firebase', + location: 'us-central1' +}; +exports.connectorConfig = connectorConfig; + +function listMoviesRef(dc) { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'ListMovies'); +} +exports.listMoviesRef = listMoviesRef; +exports.listMovies = function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +}; + +exports.injectListMovies = function injectListMovies(options) { + const dc = inject(DataConnect); + + return injectDataConnectQuery(() => { + const addOpn = options && options(); + return { + queryFn: () => ListMoviesRef(dc), + ...addOpn + }; + }); +} + + + + + + +function getMovieByIdRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +exports.getMovieByIdRef = getMovieByIdRef; +exports.getMovieById = function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +}; + +exports.injectGetMovieById = function injectGetMovieById(args, options) { + const dc = inject(DataConnect); + + const varsFactoryFn = (typeof args === 'function') ? args : () => args; + + return injectDataConnectQuery(() => { + const addOpn = options && options(); + return { + queryFn: () => GetMovieByIdRef(dc, varsFactoryFn()), + ...addOpn + }; + }); +} + + + + + + +function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'CreateMovie', inputVars); +} +exports.createMovieRef = createMovieRef; +exports.createMovie = function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +}; + +exports.injectCreateMovie = function injectCreateMovie(args) { + return injectDataConnectMutation(CreateMovieRef, args); +} + + + + + + +function upsertMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'UpsertMovie', inputVars); +} +exports.upsertMovieRef = upsertMovieRef; +exports.upsertMovie = function upsertMovie(dcOrVars, vars) { + return executeMutation(upsertMovieRef(dcOrVars, vars)); +}; + +exports.injectUpsertMovie = function injectUpsertMovie(args) { + return injectDataConnectMutation(UpsertMovieRef, args); +} + + + + + + +function deleteMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'DeleteMovie', inputVars); +} +exports.deleteMovieRef = deleteMovieRef; +exports.deleteMovie = function deleteMovie(dcOrVars, vars) { + return executeMutation(deleteMovieRef(dcOrVars, vars)); +}; + +exports.injectDeleteMovie = function injectDeleteMovie(args) { + return injectDataConnectMutation(DeleteMovieRef, args); +} + + + + + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/angular/index.d.ts b/packages/angular/dataconnect-sdk/js/default-connector/angular/index.d.ts new file mode 100644 index 00000000..98c09057 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/angular/index.d.ts @@ -0,0 +1,91 @@ +import { ListMoviesData, GetMovieByIdData, GetMovieByIdVariables, CreateMovieData, CreateMovieVariables, UpsertMovieData, UpsertMovieVariables, DeleteMovieData, DeleteMovieVariables } from '../'; +export { ListMoviesData, GetMovieByIdData, GetMovieByIdVariables, CreateMovieData, CreateMovieVariables, UpsertMovieData, UpsertMovieVariables, DeleteMovieData, DeleteMovieVariables } from '../'; +export { TimestampString, UUIDString, Int64String, DateString } from '../'; +import { ConnectorConfig, DataConnect, QueryRef, QueryPromise, MutationRef, MutationPromise} from '@angular/fire/data-connect'; +import { FlattenedQueryResult, CreateDataConnectQueryOptions, FlattenedMutationResult, CreateDataConnectMutationOptions} from '@tanstack-query-firebase/angular/data-connect'; +import { CreateQueryResult, CreateMutationResult} from '@tanstack/angular-query-experimental'; + +export const connectorConfig: ConnectorConfig; + +/* Allow users to create refs without passing in DataConnect */ +export function listMoviesRef(): QueryRef; +/* Allow users to pass in custom DataConnect instances */ +export function listMoviesRef(dc: DataConnect): QueryRef; + +export function listMovies(): QueryPromise; +export function listMovies(dc: DataConnect): QueryPromise; + + + + +type ListMoviesOptions = () => Omit, 'queryFn'>; +export function injectListMovies(options?: ListMoviesOptions): CreateQueryResult, FirebaseError, undefined>; + + + +/* Allow users to create refs without passing in DataConnect */ +export function getMovieByIdRef(vars: GetMovieByIdVariables): QueryRef; +/* Allow users to pass in custom DataConnect instances */ +export function getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; + +export function getMovieById(vars: GetMovieByIdVariables): QueryPromise; +export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; + + + + +type GetMovieByIdArgs = GetMovieByIdVariables | (() => GetMovieByIdVariables); + +type GetMovieByIdOptions = () => Omit, 'queryFn'>; +export function injectGetMovieById(args: GetMovieByIdArgs, options?: GetMovieByIdOptions): CreateQueryResult, FirebaseError, GetMovieByIdVariables>; + + + +/* Allow users to create refs without passing in DataConnect */ +export function createMovieRef(vars: CreateMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function createMovieRef(dc: DataConnect, vars: CreateMovieVariables): MutationRef; + +export function createMovie(vars: CreateMovieVariables): MutationPromise; +export function createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; + + + +type CreateMovieOptions = () => Omit, 'mutationFn'>; +export function injectCreateMovie(options?: CreateMovieOptions): CreateMutationResult; + + + +/* Allow users to create refs without passing in DataConnect */ +export function upsertMovieRef(vars: UpsertMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): MutationRef; + +export function upsertMovie(vars: UpsertMovieVariables): MutationPromise; +export function upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; + + + +type UpsertMovieOptions = () => Omit, 'mutationFn'>; +export function injectUpsertMovie(options?: UpsertMovieOptions): CreateMutationResult; + + + +/* Allow users to create refs without passing in DataConnect */ +export function deleteMovieRef(vars: DeleteMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): MutationRef; + +export function deleteMovie(vars: DeleteMovieVariables): MutationPromise; +export function deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; + + + +type DeleteMovieOptions = () => Omit, 'mutationFn'>; +export function injectDeleteMovie(options?: DeleteMovieOptions): CreateMutationResult; + + + + + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/angular/package.json b/packages/angular/dataconnect-sdk/js/default-connector/angular/package.json new file mode 100644 index 00000000..43b5d285 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/angular/package.json @@ -0,0 +1,25 @@ +{ + "name": "@dataconnect/default-connector-angular", + "version": "1.0.0", + "author": "Firebase (https://firebase.google.com/)", + "description": "Generated SDK For default-angular", + "license": "Apache-2.0", + "engines": { + "node": " >=18.0" + }, + "typings": "index.d.ts", + "main": "index.cjs.js", + "module": "esm/index.esm.js", + "browser": "esm/index.esm.js", + "exports": { + ".": { + "types": "./index.d.ts", + "require": "./index.cjs.js", + "default": "./esm/index.esm.js" + }, + "./package.json": "./package.json" + }, + "peerDependencies": { + "@angular/fire": "^19.0.0" + } +} \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/esm/index.esm.js b/packages/angular/dataconnect-sdk/js/default-connector/esm/index.esm.js new file mode 100644 index 00000000..2a710e5d --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/esm/index.esm.js @@ -0,0 +1,68 @@ +import { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } from 'firebase/data-connect'; + +export const connectorConfig = { + connector: 'default', + service: 'tanstack-query-firebase', + location: 'us-central1' +}; + +export function listMoviesRef(dc) { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'ListMovies'); +} +export function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +} +export function getMovieByIdRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +export function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +} +export function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'CreateMovie', inputVars); +} +export function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +} +export function upsertMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'UpsertMovie', inputVars); +} +export function upsertMovie(dcOrVars, vars) { + return executeMutation(upsertMovieRef(dcOrVars, vars)); +} +export function deleteMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'DeleteMovie', inputVars); +} +export function deleteMovie(dcOrVars, vars) { + return executeMutation(deleteMovieRef(dcOrVars, vars)); +} diff --git a/packages/angular/dataconnect-sdk/js/default-connector/esm/package.json b/packages/angular/dataconnect-sdk/js/default-connector/esm/package.json new file mode 100644 index 00000000..7c34deb5 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/esm/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/index.cjs.js b/packages/angular/dataconnect-sdk/js/default-connector/index.cjs.js new file mode 100644 index 00000000..0062b790 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/index.cjs.js @@ -0,0 +1,94 @@ +const { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } = require('firebase/data-connect'); + +const connectorConfig = { + connector: 'default', + service: 'tanstack-query-firebase', + location: 'us-central1' +}; +exports.connectorConfig = connectorConfig; + +function listMoviesRef(dc) { + const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'ListMovies'); +} +exports.listMoviesRef = listMoviesRef; +exports.listMovies = function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +}; + + + + +function getMovieByIdRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return queryRef(dcInstance, 'GetMovieById', inputVars); +} +exports.getMovieByIdRef = getMovieByIdRef; +exports.getMovieById = function getMovieById(dcOrVars, vars) { + return executeQuery(getMovieByIdRef(dcOrVars, vars)); +}; + + + + +function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'CreateMovie', inputVars); +} +exports.createMovieRef = createMovieRef; +exports.createMovie = function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +}; + + + + +function upsertMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'UpsertMovie', inputVars); +} +exports.upsertMovieRef = upsertMovieRef; +exports.upsertMovie = function upsertMovie(dcOrVars, vars) { + return executeMutation(upsertMovieRef(dcOrVars, vars)); +}; + + + + +function deleteMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); + if('_useGeneratedSdk' in dcInstance) { + dcInstance._useGeneratedSdk(); + } else { + console.error('Please update to the latest version of the Data Connect SDK by running `npm install firebase@dataconnect-preview`.'); + } + return mutationRef(dcInstance, 'DeleteMovie', inputVars); +} +exports.deleteMovieRef = deleteMovieRef; +exports.deleteMovie = function deleteMovie(dcOrVars, vars) { + return executeMutation(deleteMovieRef(dcOrVars, vars)); +}; + + + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/index.d.ts b/packages/angular/dataconnect-sdk/js/default-connector/index.d.ts new file mode 100644 index 00000000..fe9d2d07 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/index.d.ts @@ -0,0 +1,117 @@ +import { ConnectorConfig, DataConnect, QueryRef, QueryPromise, MutationRef, MutationPromise } from 'firebase/data-connect'; +export const connectorConfig: ConnectorConfig; + +export type TimestampString = string; + +export type UUIDString = string; + +export type Int64String = string; + +export type DateString = string; + + +export interface CreateMovieData { + movie_insert: Movie_Key; +} + +export interface CreateMovieVariables { + title: string; + genre: string; + imageUrl: string; +} + +export interface DeleteMovieData { + movie_delete?: Movie_Key | null; +} + +export interface DeleteMovieVariables { + id: UUIDString; +} + +export interface GetMovieByIdData { + movie?: { + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key; +} + +export interface GetMovieByIdVariables { + id: UUIDString; +} + +export interface ListMoviesData { + movies: ({ + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key)[]; +} + +export interface MovieMetadata_Key { + id: UUIDString; + __typename?: 'MovieMetadata_Key'; +} + +export interface Movie_Key { + id: UUIDString; + __typename?: 'Movie_Key'; +} + +export interface UpsertMovieData { + movie_upsert: Movie_Key; +} + +export interface UpsertMovieVariables { + id: UUIDString; + title: string; + imageUrl: string; +} + + +/* Allow users to create refs without passing in DataConnect */ +export function listMoviesRef(): QueryRef; +/* Allow users to pass in custom DataConnect instances */ +export function listMoviesRef(dc: DataConnect): QueryRef; + +export function listMovies(): QueryPromise; +export function listMovies(dc: DataConnect): QueryPromise; + + +/* Allow users to create refs without passing in DataConnect */ +export function getMovieByIdRef(vars: GetMovieByIdVariables): QueryRef; +/* Allow users to pass in custom DataConnect instances */ +export function getMovieByIdRef(dc: DataConnect, vars: GetMovieByIdVariables): QueryRef; + +export function getMovieById(vars: GetMovieByIdVariables): QueryPromise; +export function getMovieById(dc: DataConnect, vars: GetMovieByIdVariables): QueryPromise; + + +/* Allow users to create refs without passing in DataConnect */ +export function createMovieRef(vars: CreateMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function createMovieRef(dc: DataConnect, vars: CreateMovieVariables): MutationRef; + +export function createMovie(vars: CreateMovieVariables): MutationPromise; +export function createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; + + +/* Allow users to create refs without passing in DataConnect */ +export function upsertMovieRef(vars: UpsertMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function upsertMovieRef(dc: DataConnect, vars: UpsertMovieVariables): MutationRef; + +export function upsertMovie(vars: UpsertMovieVariables): MutationPromise; +export function upsertMovie(dc: DataConnect, vars: UpsertMovieVariables): MutationPromise; + + +/* Allow users to create refs without passing in DataConnect */ +export function deleteMovieRef(vars: DeleteMovieVariables): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function deleteMovieRef(dc: DataConnect, vars: DeleteMovieVariables): MutationRef; + +export function deleteMovie(vars: DeleteMovieVariables): MutationPromise; +export function deleteMovie(dc: DataConnect, vars: DeleteMovieVariables): MutationPromise; + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/package.json b/packages/angular/dataconnect-sdk/js/default-connector/package.json new file mode 100644 index 00000000..c9eaefe3 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/package.json @@ -0,0 +1,37 @@ +{ + "name": "@dataconnect/default-connector", + "version": "1.0.0", + "author": "Firebase (https://firebase.google.com/)", + "description": "Generated SDK For default", + "license": "Apache-2.0", + "engines": { + "node": " >=18.0" + }, + "typings": "index.d.ts", + "module": "esm/index.esm.js", + "main": "index.cjs.js", + "browser": "esm/index.esm.js", + "exports": { + ".": { + "types": "./index.d.ts", + "require": "./index.cjs.js", + "default": "./esm/index.esm.js" + }, + "./react": { + "types": "./react/index.d.ts", + "import": "./react/esm/index.esm.js", + "default": "./react/esm/index.esm.js" + }, + "./angular": { + "types": "./angular/index.d.ts", + "import": "./angular/esm/index.esm.js", + "default": "./angular/esm/index.esm.js" + }, + "./package.json": "./package.json" + }, + "peerDependencies": { + "firebase": "^10.14.0 || ^11.0.0", + "@tanstack-query-firebase/react": "^1.0.5" + + } +} \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/react/esm/index.esm.js b/packages/angular/dataconnect-sdk/js/default-connector/react/esm/index.esm.js new file mode 100644 index 00000000..1619ab92 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/react/esm/index.esm.js @@ -0,0 +1,49 @@ +import { listMoviesRef, getMovieByIdRef, createMovieRef, upsertMovieRef, deleteMovieRef } from '../../'; +import { useDataConnectQuery, useDataConnectMutation } from '@tanstack-query-firebase/react/data-connect'; + + + + + +export function useListMovies( options) { + return useDataConnectQuery(listMoviesRef(), options); +} + + + + + + +export function useGetMovieById(vars, options) { + return useDataConnectQuery(getMovieByIdRef(vars), options); +} + + + + + + +export function useCreateMovie(options) { + return useDataConnectMutation(createMovieRef, options); +} + + + + + + +export function useUpsertMovie(options) { + return useDataConnectMutation(upsertMovieRef, options); +} + + + + + + +export function useDeleteMovie(options) { + return useDataConnectMutation(deleteMovieRef, options); +} + + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/react/esm/package.json b/packages/angular/dataconnect-sdk/js/default-connector/react/esm/package.json new file mode 100644 index 00000000..7c34deb5 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/react/esm/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/packages/angular/dataconnect-sdk/js/default-connector/react/index.cjs.js b/packages/angular/dataconnect-sdk/js/default-connector/react/index.cjs.js new file mode 100644 index 00000000..137c62b7 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/react/index.cjs.js @@ -0,0 +1,50 @@ +const { listMoviesRef, getMovieByIdRef, createMovieRef, upsertMovieRef, deleteMovieRef } = require('../'); +const { useDataConnectQuery, useDataConnectMutation } = require( '@tanstack-query-firebase/react/data-connect'); + + + + + +exports.useListMovies = function useListMovies( options) { + return useDataConnectQuery(listMoviesRef(), options); +} + + + + + + +exports.useGetMovieById = function useGetMovieById(vars, options) { + return useDataConnectQuery(getMovieByIdRef(vars), options); +} + + + + + + +exports.useCreateMovie = function useCreateMovie(options) { + return useDataConnectMutation(createMovieRef, options); +} + + + + + + +exports.useUpsertMovie = function useUpsertMovie(options) { + return useDataConnectMutation(upsertMovieRef, options); +} + + + + + + +exports.useDeleteMovie = function useDeleteMovie(options) { + return useDataConnectMutation(deleteMovieRef, options); +} + + + + diff --git a/packages/angular/dataconnect-sdk/js/default-connector/react/index.d.ts b/packages/angular/dataconnect-sdk/js/default-connector/react/index.d.ts new file mode 100644 index 00000000..05ac3574 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/react/index.d.ts @@ -0,0 +1,10 @@ +import { ListMoviesData, GetMovieByIdData, GetMovieByIdVariables, CreateMovieData, CreateMovieVariables, UpsertMovieData, UpsertMovieVariables, DeleteMovieData, DeleteMovieVariables} from '../'; +import { FlattenedQueryResult, useDataConnectQueryOptions, FlattenedMutationResult, useDataConnectMutationOptions} from '@tanstack-query-firebase/react/data-connect'; +import { UseQueryResult, UseMutationResult} from '@tanstack/react-query'; +import { FirebaseError } from 'firebase/app'; + +export function useListMovies(options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; +export function useGetMovieById(vars: GetMovieByIdVariables, options?: useDataConnectQueryOptions): UseQueryResult, FirebaseError>; +export function useCreateMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, CreateMovieVariables>; +export function useUpsertMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, UpsertMovieVariables>; +export function useDeleteMovie(options?: useDataConnectMutationOptions): UseMutationResult, FirebaseError, DeleteMovieVariables>; diff --git a/packages/angular/dataconnect-sdk/js/default-connector/react/package.json b/packages/angular/dataconnect-sdk/js/default-connector/react/package.json new file mode 100644 index 00000000..f2fee700 --- /dev/null +++ b/packages/angular/dataconnect-sdk/js/default-connector/react/package.json @@ -0,0 +1,17 @@ +{ + "name": "@dataconnect/default-connector-react", + "version": "1.0.0", + "author": "Firebase (https://firebase.google.com/)", + "description": "Generated SDK For default", + "license": "Apache-2.0", + "engines": { + "node": " >=18.0" + }, + "typings": "index.d.ts", + "main": "index.cjs.js", + "module": "esm/index.esm.js", + "browser": "esm/index.esm.js", + "peerDependencies": { + "@tanstack-query-firebase/react": "^1.0.5" + } +} \ No newline at end of file diff --git a/packages/angular/index.d.ts b/packages/angular/index.d.ts new file mode 100644 index 00000000..223e65e8 --- /dev/null +++ b/packages/angular/index.d.ts @@ -0,0 +1,2 @@ + +export { } diff --git a/packages/angular/package-lock.json b/packages/angular/package-lock.json new file mode 100644 index 00000000..54385da8 --- /dev/null +++ b/packages/angular/package-lock.json @@ -0,0 +1,2174 @@ +{ + "name": "@tanstack-query-firebase/angular", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@tanstack-query-firebase/angular", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "@angular/fire": "^19.0.0-rc.5", + "@tanstack/angular-query-experimental": "^5.66.4", + "firebase": "^11.1.0" + }, + "devDependencies": { + "@analogjs/vite-plugin-angular": "^1.13.0", + "@angular/animations": "^19.0.0", + "@testing-library/angular": "^17.3.5", + "@testing-library/dom": "^10.4.0" + }, + "peerDependencies": { + "@angular/core": "^19.0.0" + } + }, + "node_modules/@analogjs/vite-plugin-angular": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@analogjs/vite-plugin-angular/-/vite-plugin-angular-1.13.1.tgz", + "integrity": "sha512-9+zd7K3Vur3UzSTu2PUdPMPrBD/JDOxz5TF2GzbqhdlWEdKL8odC7mnkKdCwzYFkcB+x9E8j4hPPpmXnMnY5Jg==", + "dev": true, + "dependencies": { + "ts-morph": "^21.0.0", + "vfile": "^6.0.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/brandonroberts" + }, + "peerDependencies": { + "@angular-devkit/build-angular": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@angular/build": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@angular-devkit/build-angular": { + "optional": true + }, + "@angular/build": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.1.8.tgz", + "integrity": "sha512-j1zHKvOsGwu5YwAZGuzi835R9vcW7PkfxmSRIJeVl+vawgk31K3zFb4UPH8AY/NPWYqXIAnwpka3HC1+JrWLWA==", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.1.8.tgz", + "integrity": "sha512-2JGUMD3zjfY8G4RYpypm2/1YEO+O4DtFycUvptIpsBYyULgnEbJ3tlp2oRiXI2vp9tC8IyWqa/swlA8DTI6ZYQ==", + "dependencies": { + "@angular-devkit/core": "19.1.8", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.1.7.tgz", + "integrity": "sha512-EnyQTCNc1nWnjc5V3HPlClpJIS2R2XAfIEUCyI3lE4FLQxcXyhIsM9NmacAZT3Ai8RL+8JVCttPmBZXMpjP6Ug==", + "devOptional": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "19.1.7" + } + }, + "node_modules/@angular/common": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.1.7.tgz", + "integrity": "sha512-MXfUGfWeesTQ12HXgeoVIXsS+r1jZxT2FkLQtqS+NRsRD4T1vlyvD7kTI+Ku1NAjdt3mB8TJ0cZHubvmml8I+Q==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "19.1.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.1.7.tgz", + "integrity": "sha512-Q3eqqIhMEzrnmFJtUO0K+WPpCfP/JTl9lJXZKe0zgNPdRFUufjSUcPHGzd7OjN2gPpiAvS1yBvENvqs+g/MejQ==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "19.1.7" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.1.7.tgz", + "integrity": "sha512-P+e4ekJYWMFhWSzJav0R51bFAfUhIOmnqmG9mlI/ZONu2qcTTmyIG9AW5x1qhrMHEH42RaeK60RkKyqgcHaGDg==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0" + } + }, + "node_modules/@angular/fire": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@angular/fire/-/fire-19.0.0.tgz", + "integrity": "sha512-dVU9IdL9sGKe1fV7g2iGgkjUQIYrbnljHtHUgNpZaskyW+Vy6ndtsEbSUoRd6sLP8WpThWH7WvITKhu/8a6qfQ==", + "dependencies": { + "@angular-devkit/schematics": "^19.0.0", + "@schematics/angular": "^19.0.0", + "firebase": "^11.2.0", + "rxfire": "^6.1.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0", + "@angular/platform-server": "^19.0.0", + "firebase-tools": "^13.0.0", + "rxjs": "~7.8.0" + }, + "peerDependenciesMeta": { + "@angular/platform-server": { + "optional": true + }, + "firebase-tools": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.7.tgz", + "integrity": "sha512-QKakWl+CeVVwn22yjRHBXm6BvDsHoo+9u1pJGGk2smKSYjHW6qAly28+P7FUfVXUQI7rg++M66JwzNOFfYMDQA==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "19.1.7", + "@angular/common": "19.1.7", + "@angular/core": "19.1.7" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.1.7.tgz", + "integrity": "sha512-rp7IXun1fHeScpIni0l2VRxBW5LzokHYfZ69K4BRhE7FpVA6hP2c9fhCJeo7681c/Q882Kom0tmsedOcF/zwMQ==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "19.1.7", + "@angular/compiler": "19.1.7", + "@angular/core": "19.1.7", + "@angular/platform-browser": "19.1.7" + } + }, + "node_modules/@angular/router": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.1.7.tgz", + "integrity": "sha512-4wqfHxVvMmnpRyRhOClo0qX7li1MnWaQ8U1ZMd8gsZgE3SKkOxXdHg/jO4tcbLMLD4QAZ82uB8MBvHl1vVMhKg==", + "dev": true, + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "19.1.7", + "@angular/core": "19.1.7", + "@angular/platform-browser": "19.1.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.11.tgz", + "integrity": "sha512-zwuPiRE0+hgcS95JZbJ6DFQN4xYFO8IyGxpeePTV51YJMwCf3lkBa6FnZ/iXIqDKcBPMgMuuEZozI0BJWaLEYg==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/installations": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.17.tgz", + "integrity": "sha512-SJNVOeTvzdqZQvXFzj7yAirXnYcLDxh57wBFROfeowq/kRN1AqOw1tG6U4OiFOEhqi7s3xLze/LMkZatk2IEww==", + "dependencies": { + "@firebase/analytics": "0.10.11", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.12", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==" + }, + "node_modules/@firebase/app": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.11.1.tgz", + "integrity": "sha512-Vz4DrNLPfDx3RwQf+4klXtu7OUYDO6xz2hlRyFawWskS7YqdtNzkDDxrqH20KDfjCF1lib46/NgchIj1+8h4wQ==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.11.tgz", + "integrity": "sha512-42zIfRI08/7bQqczAy7sY2JqZYEv3a1eNa4fLFdtJ54vNevbBIRSEA3fZgRqWFNHalh5ohsBXdrYgFqaRIuCcQ==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.18.tgz", + "integrity": "sha512-qjozwnwYmAIdrsVGrJk+hnF1WBois54IhZR6gO0wtZQoTvWL/GtiA2F31TIgAhF0ayUiZhztOv1RfC7YyrZGDQ==", + "dependencies": { + "@firebase/app-check": "0.8.11", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.50", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.50.tgz", + "integrity": "sha512-7yD362icKgjoNvFxwth420TNZgqCfuTJ28yQCdpyjC2fXyaZHhAbxVKnHEXGTAaUKSHWxsIy46lBKGi/x/Mflw==", + "dependencies": { + "@firebase/app": "0.11.1", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.9.0.tgz", + "integrity": "sha512-Xz2mbEYauF689qXG/4HppS2+/yGo9R7B6eNUBh3H2+XpAZTGdx8d8TFsW/BMTAK9Q95NB0pb1Bbvfx0lwofq8Q==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.18.tgz", + "integrity": "sha512-dFBev8AMNb2AgIt9afwf/Ku4/0Wq9R9OFSeBB/xjyJt+RfQ9PnNWqU2oFphews23byLg6jle8twRA7iOYfRGRw==", + "dependencies": { + "@firebase/auth": "1.9.0", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.12", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz", + "integrity": "sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw==", + "dependencies": { + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.0.tgz", + "integrity": "sha512-inbLq0JyQD/d02Al3Lso0Hc8z1BVpB3dYSMFcQkeKhYyjn5bspLczLdasPbCOEUp8MOkLblLZhJuRs7Q/spFnw==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.12.tgz", + "integrity": "sha512-psFl5t6rSFHq3i3fnU1QQlc4BB9Hnhh8TgEqvQlPPm8kDLw8gYxvjqYw3c5CZW0+zKR837nwT6im/wtJUivMKw==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.3.tgz", + "integrity": "sha512-uHGQrSUeJvsDfA+IyHW5O4vdRPsCksEzv4T4Jins+bmQgYy20ZESU4x01xrQCn/nzqKHuQMEW99CoCO7D+5NiQ==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/database": "1.0.12", + "@firebase/database-types": "1.0.8", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz", + "integrity": "sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.3" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.8.tgz", + "integrity": "sha512-eDvVJ/I5vSmIdGmLHJAK1OcviigIxjjia6i5/AkMFq6vZMt7CBXA0B5Xz9pGRCZ7WewFcsCbK1ZUQoYJ91+Cew==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.43", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.43.tgz", + "integrity": "sha512-zxg7YS07XQnTetGs3GADM/eA6HB4vWUp+Av4iugmTbft0fQxuTSnGm7ifctaYuR7VMTPckU9CW+oFC9QUNSYvg==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/firestore": "4.7.8", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.2.tgz", + "integrity": "sha512-iKpFDoCYk/Qm+Qwv5ynRb9/yq64QOt0A0+t9NuekyAZnSoV56kSNq/PmsVmBauar5SlmEjhHk6QKdMBP9S0gXA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.19.tgz", + "integrity": "sha512-uw4tR8NcJCDu86UD63Za8A8SgFgmAVFb1XsGlkuBY7gpLyZWEFavWnwRkZ/8cUwpqUhp/SptXFZ1WFJSnOokLw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/functions": "0.12.2", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.12.tgz", + "integrity": "sha512-ES/WpuAV2k2YtBTvdaknEo7IY8vaGjIjS3zhnHSAIvY9KwTR8XZFXOJoZ3nSkjN1A5R4MtEh+07drnzPDg9vaw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/util": "1.10.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.12.tgz", + "integrity": "sha512-RhcGknkxmFu92F6Jb3rXxv6a4sytPjJGifRZj8MSURPuv2Xu+/AispCXEfY1ZraobhEHTG5HLGsP6R4l9qB5aA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/installations": "0.6.12", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.16", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.16.tgz", + "integrity": "sha512-VJ8sCEIeP3+XkfbJA7410WhYGHdloYFZXoHe/vt+vNVDGw8JQPTQSVTRvjrUprEf5I4Tbcnpr2H34lS6zhCHSA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/installations": "0.6.12", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.10.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.16.tgz", + "integrity": "sha512-9HZZ88Ig3zQ0ok/Pwt4gQcNsOhoEy8hDHoGsV1am6ulgMuGuDVD2gl11Lere2ksL+msM12Lddi2x/7TCqmODZw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/messaging": "0.12.16", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==" + }, + "node_modules/@firebase/performance": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.0.tgz", + "integrity": "sha512-L91PwYuiJdKXKSRqsWNicvTppAJVzKjye03UlegeD6TkpKjb93T8AmJ9B0Mt0bcWHCNtnnRBCdSCvD2U9GZDjw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/installations": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.13.tgz", + "integrity": "sha512-pB0SMQj2TLQ6roDcX0YQDWvUnVgsVOl0VnUvyT/VBdCUuQYDHobZsPEuQsoEqmPA44KS/Gl0oyKqf+I8UPtRgw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.0", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.5.0.tgz", + "integrity": "sha512-weiEbpBp5PBJTHUWR4GwI7ZacaAg68BKha5QnZ8Go65W4oQjEWqCW/rfskABI/OkrGijlL3CUmCB/SA6mVo0qA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/installations": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.12.tgz", + "integrity": "sha512-91jLWPtubIuPBngg9SzwvNCWzhMLcyBccmt7TNZP+y1cuYFNOWWHKUXQ3IrxCLB7WwLqQaEu7fTDAjHsTyBsSw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.5.0", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.6.tgz", + "integrity": "sha512-BEJLYQzVgAoglRl5VRIRZ91RRBZgS/O37/PSGQJBYNuoLmFZUrtwrlLTOAwG776NlO9VQR+K2j15/36Lr2EqHA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.16.tgz", + "integrity": "sha512-EeMuok/s0r938lEomia8XILEqSYULm7HcYZ/GTZLDWur0kMf2ktuPVZiTdRiwEV3Iki7FtQO5txrQ/0pLRVLAw==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/storage": "0.13.6", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz", + "integrity": "sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/vertexai": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.0.4.tgz", + "integrity": "sha512-Nkf/r4u166b4Id6zrrW0Qtg1KyZpQvvYchtkebamnHtIfY+Qnt51I/sx4Saos/WrmO8SnrSU850LfmJ7pehYXg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@schematics/angular": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.1.8.tgz", + "integrity": "sha512-ytgClbMPn+i+w1S3QukR/Vdge+sfU9aX49ao+XRwoWdOssHUjmVjQcCEdzu0ucSrNPZnhm34bdDPzADLhln60w==", + "dependencies": { + "@angular-devkit/core": "19.1.8", + "@angular-devkit/schematics": "19.1.8", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@tanstack/angular-query-experimental": { + "version": "5.66.4", + "resolved": "https://registry.npmjs.org/@tanstack/angular-query-experimental/-/angular-query-experimental-5.66.4.tgz", + "integrity": "sha512-MELhfZLKCNMyHvAna8qFZFW5CLRpJ87qjj+e8PB1H/tbtqi9d2oWKUw6KC+XkRRVh2kauvtsyn3HBTKr063B3w==", + "dependencies": { + "@tanstack/query-core": "5.66.4", + "@tanstack/query-devtools": "5.65.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.66.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.66.4.tgz", + "integrity": "sha512-skM/gzNX4shPkqmdTCSoHtJAPMTtmIJNS0hE+xwTTUVYwezArCT34NMermABmBVUg5Ls5aiUXEDXfqwR1oVkcA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.65.0.tgz", + "integrity": "sha512-g5y7zc07U9D3esMdqUfTEVu9kMHoIaVBsD0+M3LPdAdD710RpTcLiNvJY1JkYXqkq9+NV+CQoemVNpQPBXVsJg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@testing-library/angular": { + "version": "17.3.6", + "resolved": "https://registry.npmjs.org/@testing-library/angular/-/angular-17.3.6.tgz", + "integrity": "sha512-8LxEfNtjhd6iguWus8TQngMwXfw8Y4qPMXeeCqtxbIf7gbhnPA52R8ofiqCMy+p9cD/kMOY9KZAxj18P+5vNHw==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "peerDependencies": { + "@angular/common": ">= 17.0.0", + "@angular/core": ">= 17.0.0", + "@angular/platform-browser": ">= 17.0.0", + "@angular/router": ">= 17.0.0", + "@testing-library/dom": "^10.0.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.22.0.tgz", + "integrity": "sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "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==", + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", + "dev": true + }, + "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==", + "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==" + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "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==" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/firebase": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.3.1.tgz", + "integrity": "sha512-P4YVFM0Bm2d8aO61SCEMF8E1pYgieGLrmr/LFw7vs6sAMebwuwHt+Wug+1qL2fhAHWPwpWbCLsdJH8NQ+4Sw8Q==", + "dependencies": { + "@firebase/analytics": "0.10.11", + "@firebase/analytics-compat": "0.2.17", + "@firebase/app": "0.11.1", + "@firebase/app-check": "0.8.11", + "@firebase/app-check-compat": "0.3.18", + "@firebase/app-compat": "0.2.50", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.9.0", + "@firebase/auth-compat": "0.5.18", + "@firebase/data-connect": "0.3.0", + "@firebase/database": "1.0.12", + "@firebase/database-compat": "2.0.3", + "@firebase/firestore": "4.7.8", + "@firebase/firestore-compat": "0.3.43", + "@firebase/functions": "0.12.2", + "@firebase/functions-compat": "0.3.19", + "@firebase/installations": "0.6.12", + "@firebase/installations-compat": "0.2.12", + "@firebase/messaging": "0.12.16", + "@firebase/messaging-compat": "0.2.16", + "@firebase/performance": "0.7.0", + "@firebase/performance-compat": "0.2.13", + "@firebase/remote-config": "0.5.0", + "@firebase/remote-config-compat": "0.2.12", + "@firebase/storage": "0.13.6", + "@firebase/storage-compat": "0.3.16", + "@firebase/util": "1.10.3", + "@firebase/vertexai": "1.0.4" + } + }, + "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxfire": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rxfire/-/rxfire-6.1.0.tgz", + "integrity": "sha512-NezdjeY32VZcCuGO0bbb8H8seBsJSCaWdUwGsHNzUcAOHR0VGpzgPtzjuuLXr8R/iemkqSzbx/ioS7VwV43ynA==", + "peerDependencies": { + "firebase": "^9.0.0 || ^10.0.0 || ^11.0.0", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.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==", + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-morph": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-21.0.1.tgz", + "integrity": "sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.22.0", + "code-block-writer": "^12.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "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==", + "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" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/zone.js": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", + "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", + "peer": true + } + } +} diff --git a/packages/angular/package.json b/packages/angular/package.json new file mode 100644 index 00000000..49bfc62c --- /dev/null +++ b/packages/angular/package.json @@ -0,0 +1,46 @@ +{ + "name": "@tanstack-query-firebase/angular", + "version": "0.0.1", + "description": "Tanstack Query bindings for Firebase and Angular", + "type": "module", + "files": [ + "./index.js", + "./index.d.ts", + "data-connect" + ], + "scripts": { + "build": "tsup", + "watch": "tsup --watch", + "test": "vitest --dom --coverage" + }, + "typings": "./index.d.ts", + "module": "./index.js", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js", + "default": "./data-connect/index.js" + }, + "./data-connect": { + "import": "./data-connect/index.js", + "types": "./data-connect/index.d.ts", + "default": "./data-connect/index.js" + } + }, + "author": "", + "license": "ISC", + "dependencies": { + "@angular/fire": "^19.0.0-rc.5", + "@tanstack/angular-query-experimental": "5.66.4", + "firebase": "^11.1.0" + }, + "devDependencies": { + "@angular/animations": "^19.0.0", + "@analogjs/vite-plugin-angular": "^1.13.0", + "@testing-library/angular": "^17.3.5", + "@testing-library/dom": "^10.4.0" + }, + "peerDependencies": { + "@angular/core": "^19.0.0" + } +} diff --git a/packages/angular/src/data-connect/index.ts b/packages/angular/src/data-connect/index.ts new file mode 100644 index 00000000..c7ca91cd --- /dev/null +++ b/packages/angular/src/data-connect/index.ts @@ -0,0 +1,280 @@ +import { + CreateMutationOptions, + CreateMutationResult, + CreateQueryOptions, + CreateQueryResult, + injectMutation, + injectQuery, + QueryClient, + QueryKey, +} from "@tanstack/angular-query-experimental"; + +import { FirebaseError } from "firebase/app"; + +import { + inject, + signal, +} from "@angular/core"; +import { + DataConnect, + executeMutation, + executeQuery, + MutationRef, + MutationResult, + QueryRef, + QueryResult, +} from "@angular/fire/data-connect"; +function getQueryKey(queryRef: QueryRef) { + const key: (string | Record)[] = [queryRef.name]; + if ("variables" in queryRef && queryRef.variables !== undefined) { + key.push(queryRef.variables as unknown as Record); + } + return key; +} +type FlattenedQueryResult = Omit< + QueryResult, + "data" | "toJSON" +> & + Data; +interface CreateDataConnectQueryOptions + extends Omit< + CreateQueryOptions< + FlattenedQueryResult, + FirebaseError, + FlattenedQueryResult, + QueryKey + >, + "queryFn" | "queryKey" + > { + queryFn: () => QueryRef; +} + +/** + * injectDataConnectQuery takes a query ref and returns a wrapper function around Tanstack's `injectQuery` + * @param queryRefOrOptionsFn Query Ref or callback function for calling a new query + * @returns {CreateQueryResult>} + */ +export function injectDataConnectQuery( + queryRefOrOptionsFn: + | QueryRef + | QueryRef + | (() => CreateDataConnectQueryOptions) +): CreateQueryResult, FirebaseError> { + const queryKey = signal([]); + function fdcOptionsFn() { + const passedInOptions = + typeof queryRefOrOptionsFn === "function" + ? queryRefOrOptionsFn() + : undefined; + + const modifiedFn = () => { + const ref: QueryRef = + passedInOptions?.queryFn() || + (queryRefOrOptionsFn as QueryRef); + queryKey.set([ref.name, ref.variables]); + return executeQuery(ref).then((res) => { + const { data, ...rest } = res; + return { + ...data, + ...rest, + }; + }) as Promise>; + }; + return { + queryKey: queryKey(), + ...passedInOptions, + queryFn: modifiedFn, + }; + } + return injectQuery(fdcOptionsFn); +} + +export type GeneratedSignature = ( + dc: DataConnect, + vars: Variables +) => MutationRef; // TODO(mtewani): Add __angular: true +export type DataConnectMutationOptionsFn = + () => Omit, "mutationFn"> & { + invalidate?: QueryKey | QueryRef[]; + dataConnect?: DataConnect; + mutationFn: (args: Arguments) => MutationRef; + }; +export type DataConnectMutationOptionsUndefinedMutationFn< + Data, + Error, + Variables +> = () => Omit< + ReturnType>, + "mutationFn" +>; +export type FlattenedMutationResult = Omit< + MutationResult, + "data" | "toJSON" +> & + Data; + +type EmptyFactoryFn = () => MutationRef; +export function injectDataConnectMutation( + factoryFn: undefined | null, + optionsFn: DataConnectMutationOptionsFn< + Data, + FirebaseError, + Variables, + Arguments + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +>; +export function injectDataConnectMutation< + Data, + Variables, + Arguments = void | undefined +>( + factoryFn: EmptyFactoryFn, + options?: DataConnectMutationOptionsUndefinedMutationFn< + Data, + FirebaseError, + Variables + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +>; + +export function injectDataConnectMutation< + Data, + Variables extends undefined, + Arguments = void | undefined +>( + factoryFn: EmptyFactoryFn, + options?: DataConnectMutationOptionsUndefinedMutationFn< + Data, + FirebaseError, + Variables + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +>; +export function injectDataConnectMutation< + Data, + Variables extends undefined, + Arguments = Variables +>( + factoryFn: GeneratedSignature, + optionsFn?: DataConnectMutationOptionsUndefinedMutationFn< + Data, + FirebaseError, + Arguments + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +>; +export function injectDataConnectMutation< + Data, + Variables, + Arguments extends Variables +>( + factoryFn: GeneratedSignature, + optionsFn?: DataConnectMutationOptionsUndefinedMutationFn< + Data, + FirebaseError, + Arguments + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +>; +// TODO(mtewani): Just check for the fact that the user has passed in a generated Angular fn. AKA __angular: true +/** + * injectDataConnectMutation takes a mutation ref factory function and returns a tanstack wrapper around `injectMutation` + * @example injectDataConnectMutation(createMovieRef); + * @param factoryFn generated SDK factory function + * @param optionsFn options function to create a new mutation + * @returns {CreateMutationResult, FirebaseError, Arguments>} + */ +export function injectDataConnectMutation< + Data, + Variables, + Arguments extends Variables +>( + factoryFn: + | GeneratedSignature + | EmptyFactoryFn + | undefined + | null, + optionsFn?: + | DataConnectMutationOptionsFn + | DataConnectMutationOptionsUndefinedMutationFn< + Data, + FirebaseError, + Variables + > +): CreateMutationResult< + FlattenedMutationResult, + FirebaseError, + Arguments +> { + const dataConnect = inject(DataConnect); + const queryClient = inject(QueryClient); + + const injectCb = () => { + const providedOptions = optionsFn && optionsFn(); + const modifiedFn = (args: Arguments) => { + const ref = + (providedOptions && + "mutationFn" in providedOptions! && + providedOptions!.mutationFn(args)) || + factoryFn!(dataConnect, args as Variables); + + return executeMutation(ref) + .then((res) => { + const { data, ...rest } = res; + return { + ...data, + ...rest, + }; + }) + .then((ret) => { + if (providedOptions?.invalidate) { + for (const qk of providedOptions.invalidate) { + let key = qk; + if ("name" in (key as Object)) { + const queryKey = getQueryKey(key as QueryRef); + key = queryKey; + if ( + key && + "variables" in (qk as Object) && + (qk as QueryRef).variables !== undefined + ) { + queryClient.invalidateQueries({ + queryKey: key, + exact: true, + }); + } else { + queryClient.invalidateQueries({ + queryKey: key, + }); + } + } + } + } + return ret; + }) as Promise>; + }; + + return { + ...providedOptions, + mutationFn: modifiedFn, + }; + }; + + return injectMutation(injectCb); +} diff --git a/packages/angular/src/data-connect/injectDataConnectMutation.test.ts b/packages/angular/src/data-connect/injectDataConnectMutation.test.ts new file mode 100644 index 00000000..37c27c22 --- /dev/null +++ b/packages/angular/src/data-connect/injectDataConnectMutation.test.ts @@ -0,0 +1,967 @@ +import { TestBed } from "@angular/core/testing"; +import { provideHttpClient } from '@angular/common/http'; + +import { + createMovieRef, + upsertMovieRef, + deleteMovieRef, + getMovieByIdRef, + UpsertMovieVariables, + listMoviesRef, + connectorConfig, + createMovie, +} from "@/dataconnect/default-connector/angular"; +import { waitFor } from '@testing-library/angular'; +import { beforeEach, afterEach, describe, expect, test, vi } from "vitest"; +import { injectDataConnectMutation } from "./index"; +import { + provideTanStackQuery, + QueryClient, +} from "@tanstack/angular-query-experimental"; +import { + provideDataConnect, + connectDataConnectEmulator, + DataConnect, + getDataConnect, +} from "@angular/fire/data-connect"; + +import { provideFirebaseApp, initializeApp } from "@angular/fire/app"; +import { inject, isSignal, provideExperimentalZonelessChangeDetection, Signal, untracked } from "@angular/core"; + +// initialize firebase app +initializeApp({projectId: 'p'}); + +describe("injectDataConnectMutation", () => { + let queryClient: QueryClient = new QueryClient(); + const onSuccess = vi.fn(); + let invalidateQueriesSpy = vi.spyOn(queryClient, "invalidateQueries"); + let oldMutationObserver: typeof window.MutationObserver; + + beforeEach(() => { + vi.resetAllMocks(); + oldMutationObserver = window.MutationObserver; + queryClient = new QueryClient(); + invalidateQueriesSpy = vi.spyOn(queryClient, "invalidateQueries"); + TestBed.configureTestingModule({ + providers: [ + provideExperimentalZonelessChangeDetection(), // Required as angularfire's ZoneScheduler breaks tests. + provideFirebaseApp(() => initializeApp({ projectId: "p" })), + provideDataConnect(() => { + const dc = getDataConnect(connectorConfig); + connectDataConnectEmulator(dc, "localhost", 9399); + return dc; + }), + provideHttpClient(), + provideTanStackQuery(queryClient), + ], + }); + // queryClient.clear(); + }); + + afterEach(() => { + window.MutationObserver = oldMutationObserver; + vi.restoreAllMocks(); + vi.useRealTimers() + }); + + test("returns initial state correctly for create mutation", () => { + const mutation = TestBed.runInInjectionContext(() => { + return injectDataConnectMutation(createMovieRef); + }); + expect(mutation.isIdle()).toBe(true); + expect(mutation.status()).toBe("idle"); + }); + + test("returns initial state correctly for update mutation", () => { + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(upsertMovieRef) + ); + + expect(result.isIdle()).toBe(true); + expect(result.status()).toBe("idle"); + }); + + test("returns initial state correctly for delete mutation", () => { + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(deleteMovieRef) + ); + + expect(result.isIdle()).toBe(true); + expect(result.status()).toBe("idle"); + }); + + test("executes create mutation successfully thus returning flattened data including ref, source, and fetchTime", async () => { + const mutation = TestBed.runInInjectionContext(() => { + return injectDataConnectMutation(createMovieRef); + }); + const fdc = TestBed.runInInjectionContext(() => { + return inject(DataConnect); + }); + + expect(mutation.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + await mutation.mutateAsync(movie); + + await waitFor(() => { + expect(mutation.isSuccess()).toBe(true); + expect(mutation.data()).toBeDefined(); + expect(mutation.data()).toHaveProperty("ref"); + expect(mutation.data()).toHaveProperty("source"); + expect(mutation.data()).toHaveProperty("fetchTime"); + expect(mutation.data()).toHaveProperty("movie_insert"); + expect(mutation.data()?.ref?.variables).toMatchObject(movie); + }); + }); + + test("executes update mutation successfully thus returning flattened data including ref, source, and fetchTime", async () => { + const createMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef) + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const upsertMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(upsertMovieRef) + ); + + await upsertMutationResult.mutateAsync({ + id: movieId, + imageUrl: "https://updated-image-url.com/", + title: "TanStack Query Firebase - updated", + }); + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(upsertMutationResult.data()).toBeDefined(); + expect(upsertMutationResult.data()).toHaveProperty("ref"); + expect(upsertMutationResult.data()).toHaveProperty("source"); + expect(upsertMutationResult.data()).toHaveProperty("fetchTime"); + expect(upsertMutationResult.data()).toHaveProperty("movie_upsert"); + expect(upsertMutationResult.data()?.movie_upsert.id).toBe(movieId); + }); + }); + + test("executes delete mutation successfully thus returning flattened data including ref, source, and fetchTime", async () => { + const createMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef) + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const deleteMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(deleteMovieRef) + ); + + await deleteMutationResult.mutateAsync({ + id: movieId, + }); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(deleteMutationResult.data()).toBeDefined(); + expect(deleteMutationResult.data()).toHaveProperty("ref"); + expect(deleteMutationResult.data()).toHaveProperty("source"); + expect(deleteMutationResult.data()).toHaveProperty("fetchTime"); + expect(deleteMutationResult.data()).toHaveProperty("movie_delete"); + expect(deleteMutationResult.data()?.movie_delete?.id).toBe(movieId); + }); + }); + + test("handles concurrent create mutations", async () => { + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef) + ); + + const movies = [ + { + title: "Concurrent Test 1", + genre: "concurrent_test", + imageUrl: "https://test-image-url-1.com/", + }, + { + title: "Concurrent Test 2", + genre: "concurrent_test", + imageUrl: "https://test-image-url-2.com/", + }, + { + title: "Concurrent Test 3", + genre: "concurrent_test", + imageUrl: "https://test-image-url-3.com/", + }, + ]; + + const createdMovies: { id: string }[] = []; + + await Promise.all( + movies.map(async (movie) => { + const data = await result.mutateAsync(movie); + createdMovies.push(data?.movie_insert); + }) + ); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + + // Assert that all movies were created + expect(createdMovies).toHaveLength(3); + createdMovies.forEach((movie) => { + expect(movie).toHaveProperty("id"); + }); + + // Check if all IDs are unique + const ids = createdMovies.map((movie) => movie.id); + expect(new Set(ids).size).toBe(ids.length); + }); + }); + + test("handles concurrent upsert mutations", async () => { + const createMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef) + ); + + const movies = [ + { + title: "Concurrent Test 1", + genre: "concurrent_test", + imageUrl: "https://test-image-url-1.com/", + }, + { + title: "Concurrent Test 2", + genre: "concurrent_test", + imageUrl: "https://test-image-url-2.com/", + }, + { + title: "Concurrent Test 3", + genre: "concurrent_test", + imageUrl: "https://test-image-url-3.com/", + }, + ]; + + const createdMovies: { id: string }[] = []; + + await Promise.all( + movies.map(async (movie) => { + const data = await createMutationResult.mutateAsync(movie); + createdMovies.push(data?.movie_insert); + }) + ); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + }); + + const upsertMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(upsertMovieRef) + ); + + const upsertData = createdMovies.map((movie, index) => ({ + id: movie.id, + title: `Updated Test ${index + 1}`, + imageUrl: `https://updated-image-url-${index + 1}.com/`, + })); + + // concurrent upsert operations + const upsertedMovies: { id: string }[] = []; + await Promise.all( + upsertData.map(async (update) => { + const data = await upsertMutationResult.mutateAsync(update); + upsertedMovies.push(data?.movie_upsert); + }) + ); + + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(upsertedMovies).toHaveLength(3); + + // Check if all upserted IDs match original IDs + const upsertedIds = upsertedMovies.map((movie) => movie.id); + expect(upsertedIds).toEqual( + expect.arrayContaining(createdMovies.map((m) => m.id)) + ); + }); + }); + + test("handles concurrent delete mutations", async () => { + const createMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef) + ); + + const movies = [ + { + title: "Concurrent Test 1", + genre: "concurrent_test", + imageUrl: "https://test-image-url-1.com/", + }, + { + title: "Concurrent Test 2", + genre: "concurrent_test", + imageUrl: "https://test-image-url-2.com/", + }, + { + title: "Concurrent Test 3", + genre: "concurrent_test", + imageUrl: "https://test-image-url-3.com/", + }, + ]; + + const createdMovies: { id: string }[] = []; + + await Promise.all( + movies.map(async (movie) => { + const data = await createMutationResult.mutateAsync(movie); + createdMovies.push(data?.movie_insert); + }) + ); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + }); + + const deleteMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation(deleteMovieRef) + ); + + const deleteData = createdMovies.map((movie, index) => ({ + id: movie.id, + })); + + // concurrent delete operations + const deletedMovies: { id: string }[] = []; + await Promise.all( + deleteData.map(async (i) => { + const data = await deleteMutationResult.mutateAsync(i); + deletedMovies.push(data.movie_delete!); + }) + ); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(deletedMovies).toHaveLength(3); + + // Check if all deleted IDs match original IDs + const deletedIds = deletedMovies.map((movie) => movie.id); + expect(deletedIds).toEqual( + expect.arrayContaining(createdMovies.map((m) => m.id)) + ); + }); + }); + + test("invalidates queries specified in the invalidate option for create mutations with non-variable refs", async () => { + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef, () => ({ + invalidate: [listMoviesRef()] + })) + ); + const movie = { + title: "TanStack Query Firebase", + genre: "invalidate_option_test", + imageUrl: "https://test-image-url.com/", + }; + TestBed.flushEffects(); + + // @ts-ignore + await result.mutateAsync(movie); + + await waitFor(() => { + expect(result.status()).toBe("success"); + }); + + // expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + // expect(invalidateQueriesSpy).toHaveBeenCalledWith( + // expect.objectContaining({ + // queryKey: [listMoviesRef().name], + // }) + // ); + }); + + test("invalidates queries specified in the invalidate option for create mutations with variable refs", async () => { + const movieData = { + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }; + + const createdMovie = await createMovie(movieData); + + const movieId = createdMovie?.data?.movie_insert?.id; + + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef, () => ({ + invalidate: [getMovieByIdRef({ id: movieId })], + })) + ); + const movie = { + title: "TanStack Query Firebase", + genre: "invalidate_option_test", + imageUrl: "https://test-image-url.com/", + }; + + await result.mutateAsync(movie); + + await waitFor(() => { + expect(result.status()).toBe("success"); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + expect(invalidateQueriesSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }) + ); + }); + + test("invalidates queries specified in the invalidate option for create mutations with both variable and non-variable refs", async () => { + const movieData = { + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }; + + const createdMovie = await createMovie(movieData); + + const movieId = createdMovie?.data?.movie_insert?.id; + + const result = TestBed.runInInjectionContext(() => + injectDataConnectMutation(createMovieRef, () => ({ + invalidate: [listMoviesRef(), getMovieByIdRef({ id: movieId })], + })) + ); + const movie = { + title: "TanStack Query Firebase", + genre: "invalidate_option_test", + imageUrl: "https://test-image-url.com/", + }; + + await result.mutateAsync(movie); + + await waitFor(() => { + expect(result.status()).toBe("success"); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(2); + expect(invalidateQueriesSpy.mock.calls).toEqual( + expect.arrayContaining([ + [ + expect.objectContaining({ + queryKey: ["ListMovies"], + }), + ], + [ + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }), + ], + ]) + ); + }); + + + test("invalidates queries specified in the invalidate option for upsert mutations with non-variable refs", async () => { + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + const createMutationResult = TestBed.runInInjectionContext(() => + injectDataConnectMutation( + createMovieRef + ) + ); + + + await createMutationResult.mutateAsync(movie); + + + await waitFor(() => { + expect(createMutationResult.isSuccess()).to.be.true; + }); + + + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + + const upsertMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation((_: DataConnect, vars: UpsertMovieVariables) => upsertMovieRef(vars), () => ({ + invalidate: [ listMoviesRef()], + })), + ); + + await upsertMutationResult.mutateAsync({ + id: movieId, + imageUrl: "https://updated-image-url.com/", + title: "TanStack Query Firebase - updated", + }); + upsertMutationResult.data() + + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(upsertMutationResult.data()).toHaveProperty("movie_upsert"); + expect(upsertMutationResult.data()?.movie_upsert.id).toBe(movieId); + }, { timeout: 10000}); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + expect(invalidateQueriesSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: [listMoviesRef().name], + }) + ); + }); + + test("invalidates queries specified in the invalidate option for upsert mutations with variable refs", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const upsertMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(upsertMovieRef, () => ({ + invalidate: [getMovieByIdRef({ id: movieId })], + })), + ); + + await upsertMutationResult.mutateAsync({ + id: movieId, + imageUrl: "https://updated-image-url.com/", + title: "TanStack Query Firebase - updated", + }); + + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(upsertMutationResult.data()).toHaveProperty("movie_upsert"); + expect(upsertMutationResult.data()?.movie_upsert.id).toBe(movieId); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + expect(invalidateQueriesSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }) + ); + }); + + test("invalidates queries specified in the invalidate option for upsert mutations with both variable and non-variable refs", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const upsertMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(upsertMovieRef, () => ({ + invalidate: [listMoviesRef(), getMovieByIdRef({ id: movieId })], + })), + ); + + await upsertMutationResult.mutateAsync({ + id: movieId, + imageUrl: "https://updated-image-url.com/", + title: "TanStack Query Firebase - updated", + }); + + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(upsertMutationResult.data()).toHaveProperty("movie_upsert"); + expect(upsertMutationResult.data()?.movie_upsert.id).toBe(movieId); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(2); + expect(invalidateQueriesSpy.mock.calls).toEqual( + expect.arrayContaining([ + [ + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }), + ], + [ + expect.objectContaining({ + queryKey: ["ListMovies"], + }), + ], + ]) + ); + }); + + test("invalidates queries specified in the invalidate option for delete mutations with non-variable refs", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const deleteMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(deleteMovieRef, () => ({ + invalidate: [listMoviesRef()], + })), + + ); + + await deleteMutationResult.mutateAsync({ + id: movieId, + }); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(deleteMutationResult.data()).toHaveProperty("movie_delete"); + expect(deleteMutationResult.data()?.movie_delete?.id).toBe(movieId); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + expect(invalidateQueriesSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: [listMoviesRef().name], + }) + ); + }); + + test("invalidates queries specified in the invalidate option for delete mutations with variable refs", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const deleteMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(deleteMovieRef, () => ({ + invalidate: [getMovieByIdRef({ id: movieId })], + })), + ); + + await deleteMutationResult.mutateAsync({ + id: movieId, + }); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(deleteMutationResult.data()).toHaveProperty("movie_delete"); + expect(deleteMutationResult.data()?.movie_delete?.id).toBe(movieId); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(1); + expect(invalidateQueriesSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }) + ); + }); + + test("invalidates queries specified in the invalidate option for delete mutations with both variable and non-variable refs", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const deleteMutationResult = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(deleteMovieRef, () => ({ + invalidate: [listMoviesRef(), getMovieByIdRef({ id: movieId })], + })), + ); + + await deleteMutationResult.mutateAsync({ + id: movieId, + }); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(deleteMutationResult.data()).toHaveProperty("movie_delete"); + expect(deleteMutationResult.data()?.movie_delete?.id).toBe(movieId); + }); + + expect(invalidateQueriesSpy.mock.calls).toHaveLength(2); + expect(invalidateQueriesSpy.mock.calls).toEqual( + expect.arrayContaining([ + [ + expect.objectContaining({ + queryKey: ["GetMovieById", { id: movieId }], + exact: true, + }), + ], + [ + expect.objectContaining({ + queryKey: ["ListMovies"], + }), + ], + ]) + ); + }); + + test("calls onSuccess callback after successful create mutation", async () => { + const result = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef, () => ({ onSuccess })), + ); + + const movie = { + title: "TanStack Query Firebase", + genre: "onsuccess_callback_test", + imageUrl: "https://test-image-url.com/", + }; + + await result.mutateAsync(movie); + + await waitFor(() => { + expect(onSuccess).toHaveBeenCalled(); + expect(result.isSuccess()).toBe(true); + expect(result.data()).toHaveProperty("movie_insert"); + }); + }); + + test("calls onSuccess callback after successful upsert mutation", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const upsertMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(upsertMovieRef, () => ({ onSuccess })), + ); + + await upsertMutationResult.mutateAsync({ + id: movieId, + imageUrl: "https://updated-image-url.com/", + title: "TanStack Query Firebase - updated", + }); + + await waitFor(() => { + expect(upsertMutationResult.isSuccess()).toBe(true); + expect(onSuccess).toHaveBeenCalled(); + expect(upsertMutationResult.data()).toHaveProperty("movie_upsert"); + expect(upsertMutationResult.data()?.movie_upsert.id).toBe(movieId); + }); + }); + + test("calls onSuccess callback after successful delete mutation", async () => { + const createMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(createMovieRef), + ); + + expect(createMutationResult.isIdle()).toBe(true); + + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + await createMutationResult.mutateAsync(movie); + + await waitFor(() => { + expect(createMutationResult.isSuccess()).toBe(true); + expect(createMutationResult.data()).toHaveProperty("movie_insert"); + }); + + const movieId = createMutationResult.data()?.movie_insert.id!; + + const deleteMutationResult = TestBed.runInInjectionContext( + () => injectDataConnectMutation(deleteMovieRef, () => ({ onSuccess })), + + ); + + await deleteMutationResult.mutateAsync({ + id: movieId, + }); + + await waitFor(() => { + expect(deleteMutationResult.isSuccess()).toBe(true); + expect(onSuccess).toHaveBeenCalled(); + expect(deleteMutationResult.data()).toHaveProperty("movie_delete"); + expect(deleteMutationResult.data()?.movie_delete?.id).toBe(movieId); + }); + }); + + test("executes mutation successfully with function ref", async () => { + const movie = { + title: "TanStack Query Firebase", + genre: "library", + imageUrl: "https://test-image-url.com/", + }; + + const result = TestBed.runInInjectionContext( + () => injectDataConnectMutation(() => createMovieRef(movie)), + + ); + + await result.mutateAsync(); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + expect(result.data()).toHaveProperty("movie_insert"); + expect(result.data()?.ref.variables).toMatchObject({ + title: movie.title, + genre: movie.genre, + imageUrl: movie.imageUrl, + }); + }); + }); + + test("executes mutation successfully with function ref that accepts variables", async () => { + const result = TestBed.runInInjectionContext( + () => + injectDataConnectMutation(null, () => + ({ + mutationFn: (title: string) => createMovieRef({ + title, + genre: "library", + imageUrl: "https://test-image-url.com/", + }) + }) + + ), + ); + + const movieTitle = "TanStack Query Firebase"; + + await result.mutateAsync(movieTitle); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + expect(result.data()).toHaveProperty("movie_insert"); + expect(result.data()?.ref.variables).toMatchObject({ + title: movieTitle, + genre: "library", + imageUrl: "https://test-image-url.com/", + }); + }); + }); +}); diff --git a/packages/angular/src/data-connect/injectDataConnectQuery.test.ts b/packages/angular/src/data-connect/injectDataConnectQuery.test.ts new file mode 100644 index 00000000..890c0487 --- /dev/null +++ b/packages/angular/src/data-connect/injectDataConnectQuery.test.ts @@ -0,0 +1,202 @@ +import { + connectorConfig, + createMovie, + getMovieByIdRef, + listMoviesRef, +} from "@/dataconnect/default-connector/angular"; +import { provideHttpClient } from '@angular/common/http'; +import { waitFor } from "@testing-library/angular"; +import { connectDataConnectEmulator, DataConnect, getDataConnect, provideDataConnect } from "@angular/fire/data-connect"; +import { beforeEach, describe, expect, test } from "vitest"; +import { provideFirebaseApp, initializeApp } from "@angular/fire/app"; +import {injectDataConnectQuery } from "./index"; +import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental"; +import { TestBed } from "@angular/core/testing"; +import { inject, provideExperimentalZonelessChangeDetection } from "@angular/core"; + +// initialize firebase app +initializeApp({projectId: 'p'}); + +describe("injectDataConnectQuery", () => { + let queryClient: QueryClient = new QueryClient(); + const onSuccess = vi.fn(); + let invalidateQueriesSpy = vi.spyOn(queryClient, "invalidateQueries"); + let oldMutationObserver: typeof window.MutationObserver; + let dc: DataConnect; + beforeEach(async () => { + queryClient.clear(); + TestBed.configureTestingModule({ + providers: [ + provideExperimentalZonelessChangeDetection(), // Required as angularfire's ZoneScheduler breaks tests. + provideFirebaseApp(() => initializeApp({ projectId: "p" })), + provideDataConnect(() => { + const dc = getDataConnect(connectorConfig); + connectDataConnectEmulator(dc, "localhost", 9399); + return dc; + }), + provideHttpClient(), + provideTanStackQuery(queryClient), + ], + }); + dc = TestBed.runInInjectionContext(() => inject(DataConnect)); + }); + + test("returns pending state initially", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc))); + + expect(result.isPending()).toBe(true); + expect(result.status()).toBe("pending"); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + expect(result.data()).toBeDefined(); + }); + + test("fetches data successfully", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc))); + + expect(result.isPending()).toBe(true); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + expect(result.data()).toBeDefined(); + expect(result.data()).toHaveProperty("movies"); + expect(Array.isArray(result.data()?.movies)).toBe(true); + expect(result.data()?.movies.length).toBeGreaterThanOrEqual(0); + }); + + test("refetches data successfully", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc)), ); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + expect(result.data()).toBeDefined(); + expect(result.data()).toHaveProperty("ref"); + expect(result.data()).toHaveProperty("source"); + expect(result.data()).toHaveProperty("fetchTime"); + }); + + const initialFetchTime = result.data()?.fetchTime; + + await new Promise((resolve) => setTimeout(resolve, 2000)); // 2 seconds delay before refetching + + result.refetch(); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + expect(result.data()).toBeDefined(); + expect(result.data()).toHaveProperty("ref"); + expect(result.data()).toHaveProperty("source"); + expect(result.data()).toHaveProperty("fetchTime"); + expect(result.data()).toHaveProperty("movies"); + expect(Array.isArray(result.data()?.movies)).toBe(true); + expect(result.data()?.movies.length).toBeGreaterThanOrEqual(0); + }); + + const refetchTime = result.data()?.fetchTime; + }); + + test("returns correct data", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc))); + + await createMovie({ + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + expect(result.data()).toBeDefined(); + expect(result.data()?.movies).toBeDefined(); + expect(Array.isArray(result.data()?.movies)).toBe(true); + + const movie = result.data()?.movies.find( + (m) => m.title === "tanstack query firebase", + ); + + expect(movie).toBeDefined(); + expect(movie).toHaveProperty("title", "tanstack query firebase"); + expect(movie).toHaveProperty("genre", "library"); + expect(movie).toHaveProperty("imageUrl", "https://invertase.io/"); + }); + + test("returns the correct data properties", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc))); + + await createMovie({ + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + result.data()?.movies.forEach((i) => { + expect(i).toHaveProperty("title"); + expect(i).toHaveProperty("genre"); + expect(i).toHaveProperty("imageUrl"); + }); + }); + + test("fetches data by unique identifier", async () => { + const movieData = { + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }; + const createdMovie = await createMovie(movieData); + + const movieId = createdMovie?.data?.movie_insert?.id; + + const result = TestBed.runInInjectionContext( + () => injectDataConnectQuery(getMovieByIdRef({ id: movieId })), + ); + + await waitFor(() => { + expect(result.isSuccess()).toBe(true); + expect(result.data()?.movie?.title).toBe(movieData?.title); + expect(result.data()?.movie?.genre).toBe(movieData?.genre); + expect(result.data()?.movie?.imageUrl).toBe(movieData?.imageUrl); + }); + }); + + test("returns flattened data including ref, source, and fetchTime", async () => { + const result = TestBed.runInInjectionContext(() => injectDataConnectQuery(listMoviesRef(dc))); + + expect(result.isLoading()).toBe(true); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + expect(result.data()).toBeDefined(); + expect(result.data()).toHaveProperty("ref"); + expect(result.data()).toHaveProperty("source"); + expect(result.data()).toHaveProperty("fetchTime"); + }); + + test("returns flattened data including ref, source, and fetchTime for queries with unique identifier", async () => { + const movieData = { + title: "tanstack query firebase", + genre: "library", + imageUrl: "https://invertase.io/", + }; + const createdMovie = await createMovie(movieData); + + const movieId = createdMovie?.data?.movie_insert?.id; + + const result = TestBed.runInInjectionContext( + () => injectDataConnectQuery(getMovieByIdRef({ id: movieId })) + + ); + + expect(result.isPending()).toBe(true); + + await waitFor(() => expect(result.isSuccess()).toBe(true)); + + expect(result.data()).toBeDefined(); + expect(result.data()).toHaveProperty("ref"); + expect(result.data()).toHaveProperty("source"); + expect(result.data()).toHaveProperty("fetchTime"); + }); + +}); diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts new file mode 100644 index 00000000..8cec2e9c --- /dev/null +++ b/packages/angular/src/index.ts @@ -0,0 +1 @@ +export {}; \ No newline at end of file diff --git a/packages/angular/test-setup.ts b/packages/angular/test-setup.ts new file mode 100644 index 00000000..bc6df85d --- /dev/null +++ b/packages/angular/test-setup.ts @@ -0,0 +1,10 @@ +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing' +import { getTestBed } from '@angular/core/testing' + +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +) \ No newline at end of file diff --git a/packages/angular/tsconfig.json b/packages/angular/tsconfig.json new file mode 100644 index 00000000..ce32cdaa --- /dev/null +++ b/packages/angular/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es2015", + "module": "esnext", + "moduleResolution": "node", + "declaration": true, + "jsx": "react", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "types": ["vitest/globals"], + "paths": { + "~/testing-utils": ["../react/vitest/utils.ts"], + "@/dataconnect/*": ["./dataconnect-sdk/js/*"] + } + }, + "include": ["src", "utils.tsx", "./package.json"] +} diff --git a/packages/angular/tsup.config.ts b/packages/angular/tsup.config.ts new file mode 100644 index 00000000..7c11b030 --- /dev/null +++ b/packages/angular/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsup"; + +const supportedPackages = ['data-connect']; +export default defineConfig({ + entry: [`src/data-connect/index.ts`, 'src/index.ts'], + format: ["esm"], + dts: true, // generates .d.ts files + outDir: "./", + esbuildOptions(options, context) { + options.outbase = './src'; + }, +}); diff --git a/packages/angular/vitest.config.ts b/packages/angular/vitest.config.ts new file mode 100644 index 00000000..38fb1cbf --- /dev/null +++ b/packages/angular/vitest.config.ts @@ -0,0 +1,31 @@ +import { defineConfig } from 'vitest/config' +import * as path from 'path'; + +import packageJson from './package.json' + +export default defineConfig({ + resolve: { + alias: { + "~/testing-utils": path.resolve(__dirname, "../../vitest/utils"), +"@/dataconnect/default-connector": path.resolve( + __dirname, + "./dataconnect-sdk/js/default-connector" + ), + } +, + }, + test: { + fakeTimers: { + toFake: ['setTimeout', 'clearTimeout', 'Date'] + }, + name: packageJson.name, + dir: './src', + watch: false, + environment: 'happy-dom', + setupFiles: ['test-setup.ts'], + coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] }, + typecheck: { enabled: true }, + globals: true, + restoreMocks: true, + }, +}) \ No newline at end of file diff --git a/packages/react/package.json b/packages/react/package.json index 5bb6ac73..a6896dba 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -36,10 +36,10 @@ ], "license": "Apache-2.0", "devDependencies": { + "@dataconnect/default-connector": "file:dataconnect-sdk/js/default-connector", "@testing-library/react": "^16.0.1", - "react": "^19.0.0", "@types/react": "^19.0.1", - "@dataconnect/default-connector": "workspace:*" + "react": "^19.0.0" }, "peerDependencies": { "firebase": "^11.3.0", diff --git a/packages/react/src/data-connect/useDataConnectQuery.test.tsx b/packages/react/src/data-connect/useDataConnectQuery.test.tsx index dcb37ac0..71e9f3a6 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.test.tsx +++ b/packages/react/src/data-connect/useDataConnectQuery.test.tsx @@ -1,16 +1,12 @@ -import { - createMovie, - getMovieByIdRef, - listMoviesRef, -} from "@/dataconnect/default-connector"; import { dehydrate } from "@tanstack/react-query"; import { act, renderHook, waitFor } from "@testing-library/react"; -import { executeQuery } from "firebase/data-connect"; +import { DataConnect, executeQuery, queryRef, QueryRef } from "firebase/data-connect"; import { beforeEach, describe, expect, test } from "vitest"; import { firebaseApp } from "~/testing-utils"; import { queryClient, wrapper } from "../../utils"; import { DataConnectQueryClient } from "./query-client"; import { useDataConnectQuery } from "./useDataConnectQuery"; +import { createMovie, createMovieRef, getMovieByIdRef, ListMoviesData, listMoviesRef } from "@/dataconnect/default-connector"; // initialize firebase app firebaseApp; @@ -34,7 +30,7 @@ describe("useDataConnectQuery", () => { }); test("fetches data successfully", async () => { - const { result } = renderHook(() => useDataConnectQuery(listMoviesRef()), { + const { result } = renderHook(() => useListMoviesRef(), { wrapper, }); @@ -84,7 +80,9 @@ describe("useDataConnectQuery", () => { }); test("returns correct data", async () => { - const { result } = renderHook(() => useDataConnectQuery(listMoviesRef()), { + const abc = listMoviesRef(); + useDataConnectQuery(getMovieByIdRef({ id: 'a'})) + const { result } = renderHook(() => useDataConnectQuery(abc), { wrapper, }); @@ -111,6 +109,9 @@ describe("useDataConnectQuery", () => { }); test("returns the correct data properties", async () => { + const qc = queryRef({} as DataConnect, ''); + const newQc = Object.assign(qc, {__angular: false}) + const { result } = renderHook(() => useDataConnectQuery(listMoviesRef()), { wrapper, }); @@ -251,3 +252,7 @@ describe("useDataConnectQuery", () => { ]); }); }); +function useListMoviesRef(): any { + throw new Error("Function not implemented."); +} + diff --git a/packages/react/src/data-connect/useDataConnectQuery.ts b/packages/react/src/data-connect/useDataConnectQuery.ts index 54b68799..3a0c4de5 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.ts +++ b/packages/react/src/data-connect/useDataConnectQuery.ts @@ -14,9 +14,9 @@ export type useDataConnectQueryOptions< TData = unknown, TError = FirebaseError, > = PartialBy, "queryFn">, "queryKey">; - export function useDataConnectQuery( - refOrResult: QueryRef | QueryResult, + refOrResult: QueryRef + | QueryResult, options?: useDataConnectQueryOptions< FlattenedQueryResult, FirebaseError diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index cb0abdf9..883a7ff4 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -11,8 +11,8 @@ "forceConsistentCasingInFileNames": true, "types": ["vitest/globals"], "paths": { - "~/testing-utils": ["../../vitest/utils.ts"], - "@/dataconnect/*": ["../../dataconnect-sdk/js/*"] + "~/testing-utils": ["./vitest/utils.ts"], + "@/dataconnect/*": ["./dataconnect-sdk/js/*"] } }, "include": ["src", "utils.tsx"] diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a8ee674a..10516737 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,3 @@ packages: - 'examples/*' - - 'packages/*' - - 'dataconnect-sdk/js/*' \ No newline at end of file + - 'packages/*' \ No newline at end of file diff --git a/vitest.config.ts b/vitest.config.ts deleted file mode 100644 index af98851c..00000000 --- a/vitest.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "vitest/config"; -import path from "path"; - -export default defineConfig({ - test: { - fileParallelism: false, - coverage: { - provider: "istanbul", - }, - alias: { - "~/testing-utils": path.resolve(__dirname, "./vitest/utils"), - "@/dataconnect/default-connector": path.resolve( - __dirname, - "./dataconnect-sdk/js/default-connector" - ), - }, - }, -}); diff --git a/vitest/utils.ts b/vitest/utils.ts deleted file mode 100644 index 30c4c58f..00000000 --- a/vitest/utils.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { type FirebaseApp, FirebaseError, initializeApp } from "firebase/app"; -import { getAuth, connectAuthEmulator, type Auth } from "firebase/auth"; -import { - getFirestore, - connectFirestoreEmulator, - type Firestore, -} from "firebase/firestore"; -import { expect } from "vitest"; -import { - connectDataConnectEmulator, - getDataConnect, -} from "firebase/data-connect"; -import { connectorConfig } from "@/dataconnect/default-connector"; - -const firebaseTestingOptions = { - projectId: "test-project", - apiKey: "test-api-key", - authDomain: "test-auth-domain", -}; - -let firebaseApp: FirebaseApp | undefined; -let firestore: Firestore; -let auth: Auth; - -if (!firebaseApp) { - firebaseApp = initializeApp(firebaseTestingOptions); - firestore = getFirestore(firebaseApp); - auth = getAuth(firebaseApp); - - connectFirestoreEmulator(firestore, "localhost", 8080); - connectAuthEmulator(auth, "http://localhost:9099"); - connectDataConnectEmulator( - getDataConnect(connectorConfig), - "localhost", - 9399 - ); -} - -async function wipeFirestore() { - const response = await fetch( - "http://localhost:8080/emulator/v1/projects/test-project/databases/(default)/documents", - { - method: "DELETE", - } - ); - - if (!response.ok) { - throw new Error("Failed to wipe firestore"); - } -} - -async function wipeAuth() { - const response = await fetch( - "http://localhost:9099/emulator/v1/projects/test-project/accounts", - { - method: "DELETE", - } - ); - - if (!response.ok) { - throw new Error("Failed to wipe auth"); - } -} - -function expectFirestoreError(error: unknown, expectedCode: string) { - if (error instanceof FirebaseError) { - expect(error).toBeDefined(); - expect(error.code).toBeDefined(); - expect(error.code).toBe(expectedCode); - } else { - throw new Error( - "Expected a Firestore error, but received a different type." - ); - } -} - -function expectFirebaseError(error: unknown, expectedCode: string) { - if (error instanceof FirebaseError) { - expect(error).toBeDefined(); - expect(error.code).toBeDefined(); - expect(error.code).toBe(expectedCode); - } else { - console.error("Expected a Firebase error, but received a different type.", { - receivedType: typeof error, - errorDetails: - error instanceof Error - ? { message: error.message, stack: error.stack } - : error, - }); - throw new Error( - "Expected a Firebase error, but received a different type." - ); - } -} - -export { - firestore, - wipeFirestore, - expectFirestoreError, - firebaseTestingOptions, - auth, - wipeAuth, - firebaseApp, - expectFirebaseError, -};