vuefire
Version:
Official Firebase bindings for Vue.js
1,259 lines (1,240 loc) • 37.5 kB
JavaScript
import { i as isFirestoreDataReference, a as isFirestoreQuery, b as isDatabaseReference, c as isStorageReference, n as noop, u as useFirebaseApp, d as isObject, e as checkWrittenTarget, f as useIsSSR, g as isPOJO, h as isDocumentRef, w as walkGet, j as callOnceWithArg, k as walkSet, l as getGlobalScope, s as setupOnAuthStateChanged, m as authUserMap, o as isClient, _ as _FirebaseAppInjectionKey } from './shared/vuefire.0feb90cc.mjs';
export { V as VueFireAppCheck, r as getCurrentUser, t as updateCurrentUserProfile, x as useAppCheck, v as useAppCheckToken, p as useCurrentUser, q as useIsCurrentUserLoaded } from './shared/vuefire.0feb90cc.mjs';
import { toValue, ref, shallowRef, getCurrentScope, isRef, watch, onScopeDispose, getCurrentInstance, onServerPrefetch, isVue3, toRef, effectScope, inject, computed } from 'vue-demi';
import { get, onValue, onChildAdded, onChildRemoved, onChildChanged, onChildMoved, getDatabase } from 'firebase/database';
import { Timestamp, GeoPoint, getDocs, onSnapshot, getDoc, getFirestore } from 'firebase/firestore';
import { initializeAuth, browserPopupRedirectResolver, indexedDBLocalPersistence, browserLocalPersistence, browserSessionPersistence } from 'firebase/auth';
import { getStorage, getDownloadURL, getMetadata, updateMetadata, uploadBytesResumable } from 'firebase/storage';
import 'firebase/app-check';
import 'firebase/app';
const _initialStatesMap = /* @__PURE__ */ new WeakMap();
function useSSRInitialState(initialState, firebaseApp) {
if (!_initialStatesMap.has(firebaseApp)) {
_initialStatesMap.set(
firebaseApp,
initialState || { f: {}, r: {}, s: {}, u: {} }
);
}
return _initialStatesMap.get(firebaseApp);
}
function getInitialValue(dataSource, ssrKey, fallbackValue, firebaseApp) {
if (!dataSource)
return fallbackValue;
const [sourceType, path] = getDataSourceInfo(dataSource);
if (!sourceType)
return fallbackValue;
const initialState = useSSRInitialState(void 0, firebaseApp)[sourceType] || {};
const key = ssrKey || path;
return key && key in initialState ? initialState[key] : fallbackValue;
}
function deferInitialValueSetup(dataSource, ssrKey, promise, firebaseApp) {
if (!dataSource)
return;
const [sourceType, path] = getDataSourceInfo(dataSource);
if (!sourceType)
return;
const initialState = useSSRInitialState(
void 0,
firebaseApp
)[sourceType];
const key = ssrKey || path;
if (key) {
promise.then((value) => {
initialState[key] = value;
}).catch(noop);
return key;
}
}
function getDataSourceInfo(dataSource) {
return isFirestoreDataReference(dataSource) || isFirestoreQuery(dataSource) ? ["f", dataSource.path] : isDatabaseReference(dataSource) ? ["r", dataSource.toString()] : isStorageReference(dataSource) ? ["s", dataSource.toString()] : [];
}
const appPendingPromises = /* @__PURE__ */ new WeakMap();
function addPendingPromise(promise, dataSource, ssrKey) {
const app = useFirebaseApp();
if (!appPendingPromises.has(app)) {
appPendingPromises.set(app, /* @__PURE__ */ new Map());
}
const pendingPromises = appPendingPromises.get(app);
const key = deferInitialValueSetup(dataSource, ssrKey, promise, app);
if (key) {
pendingPromises.set(key, promise);
} else {
if (process.env.NODE_ENV !== "production") {
console.warn("[VueFire SSR]: Could not get the path of the data source");
}
}
return key ? () => pendingPromises.delete(key) : noop;
}
function usePendingPromises(app) {
app = app || useFirebaseApp();
const pendingPromises = appPendingPromises.get(app);
const p = pendingPromises ? Promise.all(
Array.from(pendingPromises).map(
([key, promise]) => promise.then((data) => [key, data])
)
) : Promise.resolve([]);
appPendingPromises.delete(app);
return p;
}
function createRecordFromDatabaseSnapshot(snapshot) {
if (!snapshot.exists())
return null;
const value = snapshot.val();
return isObject(value) ? Object.defineProperty(value, "id", {
// allow destructuring without interfering without using the `id` property
value: snapshot.key
}) : {
// if the value is a primitive we can just return a regular object, it's easier to debug
// @ts-expect-error: $value doesn't exist
$value: value,
id: snapshot.key
};
}
function indexForKey(array, key) {
for (let i = 0; i < array.length; i++) {
if (array[i].id === key)
return i;
}
return -1;
}
const DEFAULT_OPTIONS$1 = {
reset: false,
serialize: createRecordFromDatabaseSnapshot,
wait: true
};
function bindAsObject(target, document, resolve, reject, extraOptions) {
const options = Object.assign({}, DEFAULT_OPTIONS$1, extraOptions);
let unsubscribe = noop;
function onValueCallback(snapshot) {
const value = options.serialize(snapshot);
target.value = value;
resolve(value);
}
if (options.once) {
get(document).then(onValueCallback).catch(reject);
} else {
unsubscribe = onValue(document, onValueCallback, reject);
}
return (reset) => {
unsubscribe();
if (reset) {
const value = typeof reset === "function" ? reset() : null;
target.value = value;
}
};
}
function bindAsArray(target, collection, resolve, reject, extraOptions) {
const options = Object.assign({}, DEFAULT_OPTIONS$1, extraOptions);
let arrayRef = options.wait ? [] : target;
if (!options.wait) {
target.value = [];
}
let removeChildAddedListener = noop;
let removeChildChangedListener = noop;
let removeChildRemovedListener = noop;
let removeChildMovedListener = noop;
let removeValueListener = noop;
if (options.once) {
get(collection).then((data) => {
const array = [];
data.forEach((snapshot) => {
array.push(options.serialize(snapshot));
});
resolve(target.value = array);
}).catch(reject);
} else {
removeChildAddedListener = onChildAdded(
collection,
(snapshot, prevKey) => {
const array = toValue(arrayRef);
const index = prevKey ? indexForKey(array, prevKey) + 1 : 0;
array.splice(index, 0, options.serialize(snapshot));
},
reject
);
removeChildRemovedListener = onChildRemoved(
collection,
(snapshot) => {
const array = toValue(arrayRef);
array.splice(indexForKey(array, snapshot.key), 1);
},
reject
);
removeChildChangedListener = onChildChanged(
collection,
(snapshot) => {
const array = toValue(arrayRef);
array.splice(
indexForKey(array, snapshot.key),
1,
// cannot be null because it exists
options.serialize(snapshot)
);
},
reject
);
removeChildMovedListener = onChildMoved(
collection,
(snapshot, prevKey) => {
const array = toValue(arrayRef);
const index = indexForKey(array, snapshot.key);
const oldRecord = array.splice(index, 1)[0];
const newIndex = prevKey ? indexForKey(array, prevKey) + 1 : 0;
array.splice(newIndex, 0, oldRecord);
},
reject
);
removeValueListener = onValue(
collection,
() => {
const array = toValue(arrayRef);
if (options.wait) {
target.value = array;
arrayRef = target;
}
resolve(array);
removeValueListener();
},
reject
);
}
return (reset) => {
removeValueListener();
removeChildAddedListener();
removeChildRemovedListener();
removeChildChangedListener();
removeChildMovedListener();
if (reset) {
const value = typeof reset === "function" ? reset() : [];
target.value = value;
}
};
}
function _useDatabaseRef(reference, localOptions = {}, isList = false) {
let unbind = noop;
const options = Object.assign({}, DEFAULT_OPTIONS$1, localOptions);
const initialSourceValue = toValue(reference);
const data = options.target || ref();
if (process.env.NODE_ENV !== "production") {
if (options.target && checkWrittenTarget(data, "useDatabaseObject()/useDatabaseList()")) {
return data;
}
}
const isSSR = useIsSSR();
if (isSSR) {
options.once = true;
}
const initialValue = getInitialValue(
initialSourceValue,
options.ssrKey,
data.value,
useFirebaseApp()
);
data.value = initialValue;
const hasInitialValue = isList ? (initialValue || []).length > 0 : initialValue !== void 0;
let shouldStartAsPending = !hasInitialValue;
const error = ref();
const pending = ref(false);
const promise = shallowRef();
const hasCurrentScope = getCurrentScope();
let removePendingPromise = noop;
function bindDatabaseRef() {
const referenceValue = toValue(reference);
const newPromise = new Promise((resolve, reject) => {
unbind(options.reset);
if (!referenceValue) {
unbind = noop;
return resolve(null);
}
pending.value = shouldStartAsPending;
shouldStartAsPending = true;
if (Array.isArray(data.value)) {
unbind = bindAsArray(
data,
referenceValue,
resolve,
reject,
options
);
} else {
unbind = bindAsObject(data, referenceValue, resolve, reject, options);
}
}).catch((reason) => {
if (promise.value === newPromise) {
error.value = reason;
}
throw reason;
}).finally(() => {
if (promise.value === newPromise) {
pending.value = false;
}
});
promise.value = newPromise;
}
let stopWatcher = noop;
if (isRef(reference) || typeof reference === "function") {
stopWatcher = watch(reference, bindDatabaseRef);
}
bindDatabaseRef();
if (initialSourceValue) {
removePendingPromise = addPendingPromise(
promise.value,
initialSourceValue,
options.ssrKey
);
}
if (hasCurrentScope) {
onScopeDispose(stop);
if (getCurrentInstance()) {
onServerPrefetch(() => promise.value);
}
}
function stop(reset = options.reset) {
stopWatcher();
removePendingPromise();
unbind(reset);
}
return Object.defineProperties(data, {
// allow destructuring without interfering with the ref itself
data: { get: () => data },
error: { get: () => error },
pending: { get: () => pending },
promise: { get: () => promise },
stop: { get: () => stop }
});
}
function useDatabaseList(reference, options) {
const data = ref([]);
return _useDatabaseRef(
reference,
{
target: data,
...options
},
true
);
}
const useList = useDatabaseList;
function useDatabaseObject(reference, options) {
const data = ref();
return _useDatabaseRef(reference, {
target: data,
...options
});
}
const useObject = useDatabaseObject;
function useDatabase(name) {
return getDatabase(useFirebaseApp(name));
}
const firestoreDefaultConverter = {
toFirestore(data) {
return data;
},
fromFirestore(snapshot, options) {
return snapshot.exists() ? Object.defineProperties(snapshot.data(options), {
id: { value: snapshot.id }
// TODO: check if worth adding or should be through an option
// It could also be an example in the docs about converters
// $meta: {
// value: snapshot.metadata,
// },
// $ref: { get: () => snapshot.ref },
}) : null;
}
};
function extractRefs(doc, oldDoc, subs, options) {
if (!isPOJO(doc))
return [doc, {}];
const dataAndRefs = [
{},
{}
];
const subsByPath = Object.keys(subs).reduce(
(resultSubs, subKey) => {
const sub = subs[subKey];
resultSubs[sub.path] = sub.data();
return resultSubs;
},
{}
);
function recursiveExtract(doc2, oldDoc2, path, result) {
oldDoc2 = oldDoc2 || {};
const [data, refs] = result;
Object.getOwnPropertyNames(doc2).forEach((propertyName) => {
const descriptor = Object.getOwnPropertyDescriptor(doc2, propertyName);
if (descriptor && !descriptor.enumerable) {
Object.defineProperty(data, propertyName, descriptor);
}
});
for (const key in doc2) {
const ref = doc2[key];
if (
// primitives
ref == null || // TODO: check and remove
// Firestore < 4.13
ref instanceof Date || ref instanceof Timestamp || ref instanceof GeoPoint
) {
data[key] = ref;
} else if (isDocumentRef(ref)) {
const refSubKey = path + key;
data[key] = // if the ref was already bound, keep the same object
// otherwise set the path as a string so it can be bound later
// https://github.com/vuejs/vuefire/issues/831
// https://github.com/vuejs/vuefire/pull/1223
refSubKey in subs ? oldDoc2[key] : ref.path;
refs[refSubKey] = ref.converter ? ref : ref.withConverter(
options.converter
);
} else if (Array.isArray(ref)) {
data[key] = Array(ref.length);
for (let i = 0; i < ref.length; i++) {
const newRef = ref[i];
if (newRef && newRef.path in subsByPath)
data[key][i] = subsByPath[newRef.path];
}
recursiveExtract(ref, oldDoc2[key] || data[key], path + key + ".", [
data[key],
refs
]);
} else if (isObject(ref)) {
data[key] = {};
recursiveExtract(ref, oldDoc2[key], path + key + ".", [data[key], refs]);
} else {
data[key] = ref;
}
}
}
recursiveExtract(doc, oldDoc, "", dataAndRefs);
return dataAndRefs;
}
const devalueCustomStringifiers = {
TimeStamp: (data) => data instanceof Timestamp && data.toJSON(),
GeoPoint: (data) => data instanceof GeoPoint && data.toJSON()
};
const devalueCustomParsers = {
TimeStamp: (data) => new Timestamp(data.seconds, data.nanoseconds),
GeoPoint: (data) => new GeoPoint(data.latitude, data.longitude)
};
const DEFAULT_OPTIONS = {
reset: false,
wait: true,
maxRefDepth: 2,
converter: firestoreDefaultConverter,
snapshotOptions: { serverTimestamps: "estimate" }
};
function unsubscribeAll(subs) {
for (const sub in subs) {
subs[sub].unsub();
}
}
function updateDataFromDocumentSnapshot(options, target, path, snapshot, subs, ops, depth, resolve, reject) {
const [data, refs] = extractRefs(
// Pass snapshot options
// @ts-expect-error: FIXME: use better types
snapshot.data(options.snapshotOptions),
walkGet(target, path),
subs,
options
);
ops.set(target, path, data);
subscribeToRefs(
options,
target,
path,
subs,
refs,
ops,
depth,
resolve,
reject
);
}
function subscribeToDocument({
ref: ref2,
target,
path,
depth,
resolve,
reject,
ops
}, options) {
const subs = /* @__PURE__ */ Object.create(null);
let unbind = noop;
if (options.once) {
getDoc(ref2).then((snapshot) => {
if (snapshot.exists()) {
updateDataFromDocumentSnapshot(
options,
target,
path,
snapshot,
subs,
ops,
depth,
resolve,
reject
);
} else {
ops.set(target, path, null);
resolve();
}
}).catch(reject);
} else {
unbind = onSnapshot(
ref2,
(snapshot) => {
if (snapshot.exists()) {
updateDataFromDocumentSnapshot(
options,
target,
path,
snapshot,
subs,
ops,
depth,
resolve,
reject
);
} else {
ops.set(target, path, null);
resolve();
}
},
reject
);
}
return () => {
unbind();
unsubscribeAll(subs);
};
}
function subscribeToRefs(options, target, path, subs, refs, ops, depth, resolve, reject) {
const refKeys = Object.keys(refs);
const missingKeys = Object.keys(subs).filter(
(refKey) => refKeys.indexOf(refKey) < 0
);
missingKeys.forEach((refKey) => {
subs[refKey].unsub();
delete subs[refKey];
});
if (!refKeys.length || ++depth > options.maxRefDepth)
return resolve(path);
let resolvedCount = 0;
const totalToResolve = refKeys.length;
const validResolves = /* @__PURE__ */ Object.create(null);
function deepResolve(key) {
if (key in validResolves) {
if (++resolvedCount >= totalToResolve)
resolve(path);
}
}
refKeys.forEach((refKey) => {
const sub = subs[refKey];
const ref2 = refs[refKey];
const docPath = `${path}.${refKey}`;
validResolves[docPath] = true;
if (sub) {
if (sub.path !== ref2.path)
sub.unsub();
else
return;
}
subs[refKey] = {
data: () => walkGet(target, docPath),
unsub: subscribeToDocument(
{
ref: ref2,
target,
path: docPath,
depth,
ops,
resolve: deepResolve.bind(null, docPath),
reject
},
options
),
path: ref2.path
};
});
}
function bindCollection(target, collection, ops, resolve, reject, extraOptions) {
const options = Object.assign({}, DEFAULT_OPTIONS, extraOptions);
const { snapshotListenOptions, snapshotOptions, wait, once } = options;
const key = "value";
let arrayRef = ref(wait ? [] : target.value);
if (!wait)
ops.set(target, key, []);
const originalResolve = resolve;
let isResolved;
let stopOnSnapshot = noop;
const arraySubs = [];
const change = {
added: ({ newIndex, doc }) => {
arraySubs.splice(newIndex, 0, /* @__PURE__ */ Object.create(null));
const subs = arraySubs[newIndex];
const [data, refs] = extractRefs(
// @ts-expect-error: FIXME: wrong cast, needs better types
doc.data(snapshotOptions),
void 0,
subs,
options
);
ops.add(toValue(arrayRef), newIndex, data);
subscribeToRefs(
options,
arrayRef,
`${key}.${newIndex}`,
subs,
refs,
ops,
0,
resolve.bind(null, doc),
reject
);
},
modified: ({ oldIndex, newIndex, doc }) => {
const array = toValue(arrayRef);
const subs = arraySubs[oldIndex];
const oldData = array[oldIndex];
const [data, refs] = extractRefs(
// @ts-expect-error: FIXME: Better types
doc.data(snapshotOptions),
oldData,
subs,
options
);
arraySubs.splice(newIndex, 0, subs);
ops.remove(array, oldIndex);
ops.add(array, newIndex, data);
subscribeToRefs(
options,
arrayRef,
`${key}.${newIndex}`,
subs,
refs,
ops,
0,
resolve,
reject
);
},
removed: ({ oldIndex }) => {
const array = toValue(arrayRef);
ops.remove(array, oldIndex);
unsubscribeAll(arraySubs.splice(oldIndex, 1)[0]);
}
};
function onSnapshotCallback(snapshot) {
const docChanges = snapshot.docChanges(snapshotListenOptions);
if (!isResolved && docChanges.length) {
isResolved = true;
let count = 0;
const expectedItems = docChanges.length;
const validDocs = /* @__PURE__ */ Object.create(null);
for (let i = 0; i < expectedItems; i++) {
validDocs[docChanges[i].doc.id] = true;
}
resolve = (data) => {
if (data && data.id in validDocs) {
if (++count >= expectedItems) {
if (wait) {
ops.set(target, key, toValue(arrayRef));
arrayRef = target;
}
originalResolve(toValue(arrayRef));
resolve = noop;
}
}
};
}
docChanges.forEach((c) => {
change[c.type](c);
});
if (!docChanges.length) {
if (wait) {
ops.set(target, key, toValue(arrayRef));
arrayRef = target;
}
resolve(toValue(arrayRef));
}
}
if (once) {
getDocs(collection).then(onSnapshotCallback).catch(reject);
} else {
stopOnSnapshot = onSnapshot(collection, onSnapshotCallback, reject);
}
return (reset) => {
stopOnSnapshot();
if (reset) {
const value = typeof reset === "function" ? reset() : [];
ops.set(target, key, value);
}
arraySubs.forEach(unsubscribeAll);
};
}
function bindDocument(target, document, ops, resolve, reject, extraOptions) {
const options = Object.assign({}, DEFAULT_OPTIONS, extraOptions);
const key = "value";
const subs = /* @__PURE__ */ Object.create(null);
resolve = callOnceWithArg(resolve, () => walkGet(target, key));
let stopOnSnapshot = noop;
function onSnapshotCallback(snapshot) {
if (snapshot.exists()) {
updateDataFromDocumentSnapshot(
options,
target,
key,
snapshot,
subs,
ops,
0,
resolve,
reject
);
} else {
ops.set(target, key, null);
resolve(null);
}
}
if (options.once) {
getDoc(document).then(onSnapshotCallback).catch(reject);
} else {
stopOnSnapshot = onSnapshot(document, onSnapshotCallback, reject);
}
return (reset) => {
stopOnSnapshot();
if (reset) {
const value = typeof reset === "function" ? reset() : null;
ops.set(target, key, value);
}
unsubscribeAll(subs);
};
}
const NO_INITIAL_VALUE = Symbol();
function _useFirestoreRef(docOrCollectionRef, localOptions) {
let unbind = noop;
const options = Object.assign({}, DEFAULT_OPTIONS, localOptions);
const initialSourceValue = toValue(docOrCollectionRef);
const data = options.target || ref();
if (process.env.NODE_ENV !== "production") {
if (options.target && checkWrittenTarget(data, "useDocument()/useCollection()")) {
return data;
}
}
if (useIsSSR()) {
options.once = true;
}
const initialValue = getInitialValue(
initialSourceValue,
options.ssrKey,
NO_INITIAL_VALUE,
useFirebaseApp()
);
const hasInitialValue = initialValue !== NO_INITIAL_VALUE;
if (hasInitialValue) {
data.value = initialValue;
}
let shouldStartAsPending = !hasInitialValue;
const pending = ref(false);
const error = ref();
const promise = shallowRef();
const hasCurrentScope = getCurrentScope();
let removePendingPromise = noop;
function bindFirestoreRef() {
let docRefValue = toValue(docOrCollectionRef);
const newPromise = new Promise((resolve, reject) => {
unbind(options.reset);
if (!docRefValue) {
unbind = noop;
return resolve(null);
}
pending.value = shouldStartAsPending;
shouldStartAsPending = true;
if (!docRefValue.converter) {
docRefValue = docRefValue.withConverter(
// @ts-expect-error: seems like a ts error
options.converter
);
}
unbind = (isDocumentRef(docRefValue) ? bindDocument : bindCollection)(
// @ts-expect-error: cannot type with the ternary
data,
docRefValue,
ops,
resolve,
reject,
options
);
}).catch((reason) => {
if (promise.value === newPromise) {
error.value = reason;
}
return Promise.reject(reason);
}).finally(() => {
if (promise.value === newPromise) {
pending.value = false;
}
});
promise.value = newPromise;
}
let stopWatcher = noop;
if (isRef(docOrCollectionRef) || typeof docOrCollectionRef === "function") {
stopWatcher = watch(docOrCollectionRef, bindFirestoreRef);
}
bindFirestoreRef();
if (initialSourceValue) {
removePendingPromise = addPendingPromise(
promise.value,
initialSourceValue,
options.ssrKey
);
}
if (getCurrentInstance()) {
onServerPrefetch(() => promise.value);
}
if (hasCurrentScope) {
onScopeDispose(stop);
}
function stop(reset = options.reset) {
stopWatcher();
removePendingPromise();
unbind(reset);
}
return Object.defineProperties(data, {
error: { get: () => error },
data: { get: () => data },
pending: { get: () => pending },
promise: { get: () => promise },
stop: { get: () => stop }
});
}
const ops = {
set: (target, key, value) => walkSet(target, key, value),
add: (array, index, data) => array.splice(index, 0, data),
remove: (array, index) => array.splice(index, 1)
};
function useCollection(collectionRef, options) {
return _useFirestoreRef(collectionRef, {
target: ref([]),
...options
});
}
function useDocument(documentRef, options) {
return _useFirestoreRef(documentRef, options);
}
function useFirestore(name) {
return getFirestore(useFirebaseApp(name));
}
const databaseUnbinds = /* @__PURE__ */ new WeakMap();
function internalUnbind$1(key, unbinds, reset) {
if (unbinds && unbinds[key]) {
unbinds[key](reset);
delete unbinds[key];
}
}
const databasePluginDefaults = {
bindName: "$databaseBind",
unbindName: "$databaseUnbind"
};
function databasePlugin(app, pluginOptions, firebaseApp) {
const globalOptions = Object.assign({}, databasePluginDefaults, pluginOptions);
const { bindName, unbindName } = globalOptions;
const GlobalTarget = isVue3 ? app.config.globalProperties : app.prototype;
GlobalTarget[unbindName] = function databaseUnbind(key, reset) {
internalUnbind$1(key, databaseUnbinds.get(this), reset);
delete this.$firebaseRefs[key];
};
GlobalTarget[bindName] = function databaseBind(key, source, userOptions) {
const options = Object.assign({}, globalOptions, userOptions);
const target = toRef(this.$data, key);
if (!databaseUnbinds.has(this)) {
databaseUnbinds.set(this, {});
}
const unbinds = databaseUnbinds.get(this);
if (unbinds[key]) {
unbinds[key](options.reset);
}
if (pluginOptions) {
if (!pluginOptions.bindName) {
GlobalTarget["$rtdbBind"] = GlobalTarget[bindName];
}
if (!pluginOptions.unbindName) {
GlobalTarget["$rtdbUnbind"] = GlobalTarget[unbindName];
}
}
const scope = getGlobalScope(firebaseApp || useFirebaseApp(), app).run(
() => effectScope()
);
const { promise, stop: _unbind } = app.runWithContext(
() => scope.run(() => _useDatabaseRef(source, { target, ...options }))
);
const unbind = (reset) => {
_unbind(reset);
scope.stop();
};
unbinds[key] = unbind;
this.$firebaseRefs[key] = source.ref;
return promise.value;
};
app.mixin({
beforeCreate() {
this.$firebaseRefs = /* @__PURE__ */ Object.create(null);
},
created() {
let bindings = this.$options.firebase;
if (typeof bindings === "function") {
bindings = bindings.call(this);
}
if (!bindings)
return;
for (const key in bindings) {
this[bindName](
// ts
key,
bindings[key],
globalOptions
);
}
},
beforeUnmount() {
const unbinds = databaseUnbinds.get(this);
if (unbinds) {
for (const key in unbinds) {
unbinds[key]();
}
}
this.$firebaseRefs = null;
}
});
}
function VueFireDatabaseOptionsAPI(pluginOptions) {
return (firebaseApp, app) => {
return databasePlugin(app, pluginOptions, firebaseApp);
};
}
const firestoreUnbinds = /* @__PURE__ */ new WeakMap();
function internalUnbind(key, unbinds, reset) {
if (unbinds && unbinds[key]) {
unbinds[key](reset);
delete unbinds[key];
}
}
const firestorePluginDefaults = {
bindName: "$firestoreBind",
unbindName: "$firestoreUnbind"
};
const firestorePlugin = function firestorePlugin2(app, pluginOptions, firebaseApp) {
const globalOptions = Object.assign(
{},
firestorePluginDefaults,
pluginOptions
);
const { bindName, unbindName } = globalOptions;
const GlobalTarget = isVue3 ? app.config.globalProperties : app.prototype;
GlobalTarget[unbindName] = function firestoreUnbind(key, reset) {
internalUnbind(key, firestoreUnbinds.get(this), reset);
delete this.$firestoreRefs[key];
};
GlobalTarget[bindName] = function firestoreBind(key, docOrCollectionRef, userOptions) {
const options = Object.assign({}, globalOptions, userOptions);
const target = toRef(this.$data, key);
if (!firestoreUnbinds.has(this)) {
firestoreUnbinds.set(this, {});
}
const unbinds = firestoreUnbinds.get(this);
if (unbinds[key]) {
unbinds[key](options.reset);
}
const scope = getGlobalScope(firebaseApp || useFirebaseApp(), app).run(
() => effectScope()
);
const { promise, stop: _unbind } = app.runWithContext(
() => scope.run(
() => _useFirestoreRef(docOrCollectionRef, {
target,
...options
})
)
);
const unbind = (reset) => {
_unbind(reset);
scope.stop();
};
unbinds[key] = unbind;
this.$firestoreRefs[key] = // ts
docOrCollectionRef;
return promise.value;
};
app.mixin({
beforeCreate() {
this.$firestoreRefs = /* @__PURE__ */ Object.create(null);
},
created() {
const { firestore } = this.$options;
const refs = typeof firestore === "function" ? firestore.call(this) : firestore;
if (!refs)
return;
for (const key in refs) {
this[bindName](
key,
// @ts-expect-error: FIXME: there is probably a wrong type in global properties
refs[key],
globalOptions
);
}
},
beforeUnmount() {
const unbinds = firestoreUnbinds.get(this);
if (unbinds) {
for (const subKey in unbinds) {
unbinds[subKey]();
}
}
this.$firestoreRefs = null;
}
});
};
function VueFireFirestoreOptionsAPI(pluginOptions) {
return (firebaseApp, app) => {
return firestorePlugin(app, pluginOptions, firebaseApp);
};
}
function VueFireAuth(initialUser) {
return VueFireAuthWithDependencies({
initialUser,
dependencies: {
popupRedirectResolver: browserPopupRedirectResolver,
persistence: [
indexedDBLocalPersistence,
browserLocalPersistence,
browserSessionPersistence
]
}
});
}
const _VueFireAuthKey = Symbol("VueFireAuth");
function VueFireAuthOptionsFromAuth({
auth,
initialUser
}) {
return (firebaseApp, app) => {
const [user, _auth] = _VueFireAuthInit(
firebaseApp,
app,
initialUser,
void 0,
auth
);
setupOnAuthStateChanged(user, _auth);
};
}
function VueFireAuthWithDependencies({
dependencies,
initialUser
}) {
return (firebaseApp, app) => {
const [user, auth] = _VueFireAuthInit(
firebaseApp,
app,
initialUser,
dependencies
);
setupOnAuthStateChanged(user, auth);
};
}
function _VueFireAuthInit(firebaseApp, app, initialUser, dependencies, auth = initializeAuth(firebaseApp, dependencies)) {
const user = getGlobalScope(firebaseApp, app).run(
() => ref(initialUser)
);
authUserMap.set(firebaseApp, user);
app.provide(_VueFireAuthKey, auth);
return [user, auth];
}
function useFirebaseAuth(name) {
if (process.env.NODE_ENV !== "production" && name != null) {
console.warn(
`[VueFire] useFirebaseAuth() no longer accepts a name parameter to enable tree shaking. If you have multiple applications, you must use "getAuth(firebaseApp)" or "getAuth(useFirebaseApp(name))" instead.`
);
}
return isClient ? inject(_VueFireAuthKey) : null;
}
function useFirebaseStorage(name) {
return getStorage(useFirebaseApp(name));
}
function useStorageFileUrl(storageRef) {
const initialSourceValue = toValue(storageRef);
const url = ref();
url.value = getInitialValue(
initialSourceValue,
void 0,
url.value,
useFirebaseApp()
);
const promise = shallowRef(Promise.resolve(null));
let removePendingPromise = noop;
function refresh() {
const storageSource = toValue(storageRef);
if (storageSource) {
promise.value = getDownloadURL(storageSource).then((downloadUrl) => url.value = downloadUrl).catch(() => null);
} else {
promise.value = Promise.resolve(url.value = null);
}
return promise.value;
}
refresh();
if (isRef(storageRef) || typeof storageRef === "function") {
watch(storageRef, refresh);
}
if (initialSourceValue) {
removePendingPromise = addPendingPromise(promise.value, initialSourceValue);
}
if (getCurrentScope()) {
onScopeDispose(removePendingPromise);
}
if (getCurrentInstance()) {
onServerPrefetch(() => promise.value);
}
return { url, refresh, promise };
}
function useStorageFileMetadata(storageRef) {
const initialSourceValue = toValue(storageRef);
const metadata = shallowRef();
if (initialSourceValue) {
metadata.value = getInitialValue(
initialSourceValue,
// 'm ' is a prefix to differentiate from urls since both are stored in the same object
"m " + initialSourceValue.toString(),
metadata.value,
useFirebaseApp()
);
}
const promise = shallowRef(
Promise.resolve(null)
);
let removePendingPromise = noop;
function refresh() {
const storageSource = toValue(storageRef);
if (storageSource) {
promise.value = getMetadata(storageSource).then((data) => metadata.value = data).catch(() => null);
} else {
promise.value = Promise.resolve(metadata.value = null);
}
return promise.value;
}
function update(newMetadata) {
const storageSource = toValue(storageRef);
if (storageSource) {
promise.value = updateMetadata(storageSource, newMetadata).then(
(newData) => {
return metadata.value = newData;
}
);
} else if (process.env.NODE_ENV !== "production") {
console.warn('[VueFire]: "update()" called with no storage source.');
}
return promise.value;
}
refresh();
if (isRef(storageRef)) {
watch(storageRef, refresh);
}
if (initialSourceValue) {
removePendingPromise = addPendingPromise(promise.value, initialSourceValue);
}
if (getCurrentScope()) {
onScopeDispose(removePendingPromise);
}
if (getCurrentInstance()) {
onServerPrefetch(() => promise.value);
}
return { metadata, update, refresh, promise };
}
function useStorageFile(storageRef) {
const { url, refresh: refreshUrl } = useStorageFileUrl(storageRef);
const {
metadata,
update: updateMetadata2,
refresh: refreshMetadata
} = useStorageFileMetadata(storageRef);
const uploadTask = shallowRef();
const snapshot = shallowRef();
const uploadError = shallowRef();
const uploadProgress = computed(() => {
const snap = toValue(snapshot);
return snap ? snap.bytesTransferred / snap.totalBytes : null;
});
let unsub = noop;
function upload(newData, newMetadata) {
const storageSource = toValue(storageRef);
const currentTask = toValue(uploadTask);
if (currentTask) {
currentTask.cancel();
}
uploadError.value = null;
snapshot.value = null;
uploadTask.value = null;
url.value = null;
metadata.value = null;
unsub();
if (storageSource) {
const newTask = uploadBytesResumable(storageSource, newData, newMetadata);
uploadTask.value = newTask;
snapshot.value = newTask.snapshot;
unsub = newTask.on("state_changed", (newSnapshot) => {
snapshot.value = newSnapshot;
});
return newTask.then((finalSnapshot) => {
metadata.value = finalSnapshot.metadata;
refreshUrl();
}).catch((err) => {
uploadError.value = err;
return Promise.reject(err);
}).finally(() => {
unsub();
uploadTask.value = null;
});
}
}
function refresh() {
return Promise.all([refreshUrl(), refreshMetadata()]);
}
if (isRef(storageRef) || typeof storageRef === "function") {
watch(storageRef, (storageSource) => {
if (!storageSource) {
if (uploadTask.value) {
unsub();
uploadTask.value.cancel();
}
uploadTask.value = null;
snapshot.value = null;
}
refresh();
});
}
if (getCurrentScope()) {
onScopeDispose(unsub);
}
return {
url,
metadata,
snapshot,
uploadTask,
uploadError,
uploadProgress,
upload,
updateMetadata: updateMetadata2,
refresh
// promise,
};
}
const useStorage = useFirebaseStorage;
const useStorageUrl = useStorageFileUrl;
const useStorageMetadata = useStorageFileMetadata;
const useStorageObject = useStorageFile;
function VueFire(app, { firebaseApp, modules = [] }) {
app.provide(_FirebaseAppInjectionKey, firebaseApp);
for (const firebaseModule of modules) {
firebaseModule(firebaseApp, app);
}
}
export { VueFire, VueFireAuth, VueFireAuthOptionsFromAuth, VueFireAuthWithDependencies, VueFireDatabaseOptionsAPI, VueFireFirestoreOptionsAPI, _VueFireAuthInit, _VueFireAuthKey, createRecordFromDatabaseSnapshot as databaseDefaultSerializer, databasePlugin, devalueCustomParsers, devalueCustomStringifiers, firestoreDefaultConverter, firestorePlugin, DEFAULT_OPTIONS$1 as globalDatabaseOptions, DEFAULT_OPTIONS as globalFirestoreOptions, databasePlugin as rtdbPlugin, useCollection, useDatabase, useDatabaseList, useDatabaseObject, useDocument, useFirebaseApp, useFirebaseAuth, useFirebaseStorage, useFirestore, useList, useObject, usePendingPromises, useSSRInitialState, useStorage, useStorageFile, useStorageFileMetadata, useStorageFileUrl, useStorageMetadata, useStorageObject, useStorageUrl };