@olinfo/quizms-firebase 4.0.0-canary.20251130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bin.js +4 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +36 -0
- package/dist/cli.js.map +1 -0
- package/dist/entry.d.ts +4 -0
- package/dist/entry.js +19 -0
- package/dist/entry.js.map +1 -0
- package/dist/functions.d.ts +14 -0
- package/dist/functions.js +104 -0
- package/dist/functions.js.map +1 -0
- package/dist/package.json.js +5 -0
- package/dist/package.json.js.map +1 -0
- package/dist/src/cli/definalize.js +23 -0
- package/dist/src/cli/definalize.js.map +1 -0
- package/dist/src/cli/export.js +54 -0
- package/dist/src/cli/export.js.map +1 -0
- package/dist/src/cli/files/firestore-indexes.json.js +10 -0
- package/dist/src/cli/files/firestore-indexes.json.js.map +1 -0
- package/dist/src/cli/files/firestore.rules.js +191 -0
- package/dist/src/cli/files/firestore.rules.js.map +1 -0
- package/dist/src/cli/files/storage.rules.js +35 -0
- package/dist/src/cli/files/storage.rules.js.map +1 -0
- package/dist/src/cli/import.js +140 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.js +140 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/update-scores.js +37 -0
- package/dist/src/cli/update-scores.js.map +1 -0
- package/dist/src/cli/utils/collection.js +55 -0
- package/dist/src/cli/utils/collection.js.map +1 -0
- package/dist/src/cli/utils/converters-admin.js +41 -0
- package/dist/src/cli/utils/converters-admin.js.map +1 -0
- package/dist/src/cli/utils/initialize.js +31 -0
- package/dist/src/cli/utils/initialize.js.map +1 -0
- package/dist/src/cli/utils/rest-api.js +23 -0
- package/dist/src/cli/utils/rest-api.js.map +1 -0
- package/dist/src/cli/utils/storage.js +41 -0
- package/dist/src/cli/utils/storage.js.map +1 -0
- package/dist/src/cli/utils/users.js +30 -0
- package/dist/src/cli/utils/users.js.map +1 -0
- package/dist/src/models/user.js +12 -0
- package/dist/src/models/user.js.map +1 -0
- package/dist/src/vite/entry.js +33 -0
- package/dist/src/vite/entry.js.map +1 -0
- package/dist/src/vite/preset.js +30 -0
- package/dist/src/vite/preset.js.map +1 -0
- package/dist/src/web/admin/announcements.js +85 -0
- package/dist/src/web/admin/announcements.js.map +1 -0
- package/dist/src/web/admin/contest-settings.js +16 -0
- package/dist/src/web/admin/contest-settings.js.map +1 -0
- package/dist/src/web/admin/context.js +11 -0
- package/dist/src/web/admin/context.js.map +1 -0
- package/dist/src/web/admin/dashboard.js +85 -0
- package/dist/src/web/admin/dashboard.js.map +1 -0
- package/dist/src/web/admin/export.js +59 -0
- package/dist/src/web/admin/export.js.map +1 -0
- package/dist/src/web/admin/layout.js +38 -0
- package/dist/src/web/admin/layout.js.map +1 -0
- package/dist/src/web/admin/provider.js +30 -0
- package/dist/src/web/admin/provider.js.map +1 -0
- package/dist/src/web/admin/school-table.js +79 -0
- package/dist/src/web/admin/school-table.js.map +1 -0
- package/dist/src/web/common/api.js +43 -0
- package/dist/src/web/common/api.js.map +1 -0
- package/dist/src/web/common/base-login.js +26 -0
- package/dist/src/web/common/base-login.js.map +1 -0
- package/dist/src/web/common/converters.js +57 -0
- package/dist/src/web/common/converters.js.map +1 -0
- package/dist/src/web/common/query.js +16 -0
- package/dist/src/web/common/query.js.map +1 -0
- package/dist/src/web/common/token-login.js +40 -0
- package/dist/src/web/common/token-login.js.map +1 -0
- package/dist/src/web/entry/admin.js +27 -0
- package/dist/src/web/entry/admin.js.map +1 -0
- package/dist/src/web/entry/student-statement.js +28 -0
- package/dist/src/web/entry/student-statement.js.map +1 -0
- package/dist/src/web/entry/student.js +126 -0
- package/dist/src/web/entry/student.js.map +1 -0
- package/dist/src/web/entry/teacher.js +88 -0
- package/dist/src/web/entry/teacher.js.map +1 -0
- package/dist/src/web/hooks/auth.js +26 -0
- package/dist/src/web/hooks/auth.js.map +1 -0
- package/dist/src/web/hooks/collection.js +68 -0
- package/dist/src/web/hooks/collection.js.map +1 -0
- package/dist/src/web/hooks/count.js +22 -0
- package/dist/src/web/hooks/count.js.map +1 -0
- package/dist/src/web/hooks/document.js +61 -0
- package/dist/src/web/hooks/document.js.map +1 -0
- package/dist/src/web/hooks/subscription.js +42 -0
- package/dist/src/web/hooks/subscription.js.map +1 -0
- package/dist/vite.d.ts +4 -0
- package/dist/vite.js +7 -0
- package/dist/vite.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useCallback as C } from "react";
|
|
2
|
+
import { collection as g, getDocs as v, doc as O, setDoc as S, onSnapshot as B } from "firebase/firestore";
|
|
3
|
+
import { sortBy as k } from "lodash-es";
|
|
4
|
+
import { useErrorBoundary as R } from "react-error-boundary";
|
|
5
|
+
import q from "swr";
|
|
6
|
+
import { useDb as E } from "../common/base-login.js";
|
|
7
|
+
import $ from "../common/query.js";
|
|
8
|
+
import { useSubscriptionListener as w } from "./subscription.js";
|
|
9
|
+
const d = {
|
|
10
|
+
revalidate: !1,
|
|
11
|
+
rollbackOnError: !0,
|
|
12
|
+
populateCache: !0
|
|
13
|
+
};
|
|
14
|
+
function j(s, t, r) {
|
|
15
|
+
const e = E(), { showBoundary: a } = R(), c = g(e, s).withConverter(t), m = $(e, s, t, r), b = {
|
|
16
|
+
revalidateIfStale: !r?.subscribe,
|
|
17
|
+
revalidateOnFocus: !r?.subscribe,
|
|
18
|
+
revalidateOnMount: !r?.subscribe,
|
|
19
|
+
revalidateOnReconnect: !r?.subscribe,
|
|
20
|
+
suspense: !0
|
|
21
|
+
}, f = `${s}?${JSON.stringify(r)}`, { data: y, mutate: o } = q(f, () => D(m), b), h = C(
|
|
22
|
+
(i) => x(c, i, o, r),
|
|
23
|
+
[c, o, r]
|
|
24
|
+
);
|
|
25
|
+
return w(
|
|
26
|
+
f,
|
|
27
|
+
(i) => {
|
|
28
|
+
if (!r?.subscribe) return;
|
|
29
|
+
const p = B(
|
|
30
|
+
m,
|
|
31
|
+
(u) => {
|
|
32
|
+
try {
|
|
33
|
+
i(u.docs.map((n) => n.data()));
|
|
34
|
+
} catch (n) {
|
|
35
|
+
a(n);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
(u) => a(u)
|
|
39
|
+
);
|
|
40
|
+
return () => p();
|
|
41
|
+
},
|
|
42
|
+
(i) => o(i, d)
|
|
43
|
+
), [y, h];
|
|
44
|
+
}
|
|
45
|
+
async function x(s, t, r, e) {
|
|
46
|
+
await r(
|
|
47
|
+
async (a) => {
|
|
48
|
+
const c = O(s, t.id);
|
|
49
|
+
return await S(c, t), l(a, t, e);
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
...d,
|
|
53
|
+
optimisticData: (a) => l(a, t, e)
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function l(s, t, r) {
|
|
58
|
+
if (!s) return [t];
|
|
59
|
+
let e = s.filter((a) => t.id !== a.id);
|
|
60
|
+
return e.push(t), e = k(e, [r?.orderBy ?? "id"]), r?.limit && (e = e.slice(0, r.limit)), e;
|
|
61
|
+
}
|
|
62
|
+
async function D(s) {
|
|
63
|
+
return (await v(s)).docs.map((r) => r.data({ serverTimestamps: "estimate" }));
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
j as useCollection
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sources":["../../../../src/web/hooks/collection.ts"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport {\n type CollectionReference,\n collection,\n doc,\n type FirestoreDataConverter,\n getDocs,\n onSnapshot,\n type Query,\n setDoc,\n} from \"firebase/firestore\";\nimport { sortBy } from \"lodash-es\";\nimport { useErrorBoundary } from \"react-error-boundary\";\nimport useSWR, { type KeyedMutator, type MutatorOptions, type SWRConfiguration } from \"swr\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport query, { type QueryOption } from \"~/web/common/query\";\n\nimport { useSubscriptionListener } from \"./subscription\";\n\nconst mutationConfig: MutatorOptions = {\n revalidate: false,\n rollbackOnError: true,\n populateCache: true,\n};\n\ntype CollectionOptions<T> = QueryOption<T> & {\n subscribe?: boolean;\n};\n\nexport function useCollection<T extends { id: string }>(\n path: string,\n converter: FirestoreDataConverter<T>,\n options?: CollectionOptions<T>,\n) {\n const db = useDb();\n const { showBoundary } = useErrorBoundary();\n\n const ref = collection(db, path).withConverter(converter);\n const q = query(db, path, converter, options);\n\n const swrConfig: SWRConfiguration = {\n revalidateIfStale: !options?.subscribe,\n revalidateOnFocus: !options?.subscribe,\n revalidateOnMount: !options?.subscribe,\n revalidateOnReconnect: !options?.subscribe,\n suspense: true,\n };\n\n const key = `${path}?${JSON.stringify(options)}`;\n const { data, mutate } = useSWR<T[]>(key, () => fetcher(q), swrConfig);\n\n const setData = useCallback(\n (newDoc: T) => setDocument(ref, newDoc, mutate, options),\n [ref, mutate, options],\n );\n\n useSubscriptionListener<T[]>(\n key,\n (setData) => {\n if (!options?.subscribe) return;\n const unsubscribe = onSnapshot(\n q,\n (snap) => {\n try {\n setData(snap.docs.map((doc) => doc.data()));\n } catch (err) {\n showBoundary(err);\n }\n },\n (error) => showBoundary(error),\n );\n return () => unsubscribe();\n },\n (data) => mutate(data, mutationConfig),\n );\n\n return [data as T[], setData] as const;\n}\n\nasync function setDocument<T extends { id: string }>(\n ref: CollectionReference<T>,\n newDoc: T,\n mutator: KeyedMutator<T[]>,\n options?: CollectionOptions<T>,\n): Promise<void> {\n await mutator(\n async (prev) => {\n const docRef = doc(ref, newDoc.id);\n await setDoc(docRef, newDoc);\n return merge(prev, newDoc, options);\n },\n {\n ...mutationConfig,\n optimisticData: (prev) => merge(prev, newDoc, options),\n },\n );\n}\n\nfunction merge<T extends { id: string }>(\n prev: T[] | undefined,\n newDoc: T,\n options?: CollectionOptions<T>,\n) {\n if (!prev) return [newDoc];\n let coll = prev.filter((doc) => newDoc.id !== doc.id);\n coll.push(newDoc);\n coll = sortBy(coll, [options?.orderBy ?? \"id\"]);\n if (options?.limit) coll = coll.slice(0, options.limit);\n return coll;\n}\n\nasync function fetcher<T>(ref: Query<T>) {\n const snapshot = await getDocs(ref);\n return snapshot.docs.map((doc) => doc.data({ serverTimestamps: \"estimate\" }));\n}\n"],"names":["mutationConfig","useCollection","path","converter","options","db","useDb","showBoundary","useErrorBoundary","ref","collection","q","query","swrConfig","key","data","mutate","useSWR","fetcher","setData","useCallback","newDoc","setDocument","useSubscriptionListener","unsubscribe","onSnapshot","snap","doc","err","error","mutator","prev","docRef","setDoc","merge","coll","sortBy","getDocs"],"mappings":";;;;;;;;AAqBA,MAAMA,IAAiC;AAAA,EACrC,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,eAAe;AACjB;AAMO,SAASC,EACdC,GACAC,GACAC,GACA;AACA,QAAMC,IAAKC,EAAA,GACL,EAAE,cAAAC,EAAA,IAAiBC,EAAA,GAEnBC,IAAMC,EAAWL,GAAIH,CAAI,EAAE,cAAcC,CAAS,GAClDQ,IAAIC,EAAMP,GAAIH,GAAMC,GAAWC,CAAO,GAEtCS,IAA8B;AAAA,IAClC,mBAAmB,CAACT,GAAS;AAAA,IAC7B,mBAAmB,CAACA,GAAS;AAAA,IAC7B,mBAAmB,CAACA,GAAS;AAAA,IAC7B,uBAAuB,CAACA,GAAS;AAAA,IACjC,UAAU;AAAA,EAAA,GAGNU,IAAM,GAAGZ,CAAI,IAAI,KAAK,UAAUE,CAAO,CAAC,IACxC,EAAE,MAAAW,GAAM,QAAAC,EAAA,IAAWC,EAAYH,GAAK,MAAMI,EAAQP,CAAC,GAAGE,CAAS,GAE/DM,IAAUC;AAAA,IACd,CAACC,MAAcC,EAAYb,GAAKY,GAAQL,GAAQZ,CAAO;AAAA,IACvD,CAACK,GAAKO,GAAQZ,CAAO;AAAA,EAAA;AAGvB,SAAAmB;AAAA,IACET;AAAA,IACA,CAACK,MAAY;AACX,UAAI,CAACf,GAAS,UAAW;AACzB,YAAMoB,IAAcC;AAAA,QAClBd;AAAA,QACA,CAACe,MAAS;AACR,cAAI;AACFP,YAAAA,EAAQO,EAAK,KAAK,IAAI,CAACC,MAAQA,EAAI,KAAA,CAAM,CAAC;AAAA,UAC5C,SAASC,GAAK;AACZ,YAAArB,EAAaqB,CAAG;AAAA,UAClB;AAAA,QACF;AAAA,QACA,CAACC,MAAUtB,EAAasB,CAAK;AAAA,MAAA;AAE/B,aAAO,MAAML,EAAA;AAAA,IACf;AAAA,IACA,CAACT,MAASC,EAAOD,GAAMf,CAAc;AAAA,EAAA,GAGhC,CAACe,GAAaI,CAAO;AAC9B;AAEA,eAAeG,EACbb,GACAY,GACAS,GACA1B,GACe;AACf,QAAM0B;AAAA,IACJ,OAAOC,MAAS;AACd,YAAMC,IAASL,EAAIlB,GAAKY,EAAO,EAAE;AACjC,mBAAMY,EAAOD,GAAQX,CAAM,GACpBa,EAAMH,GAAMV,GAAQjB,CAAO;AAAA,IACpC;AAAA,IACA;AAAA,MACE,GAAGJ;AAAA,MACH,gBAAgB,CAAC+B,MAASG,EAAMH,GAAMV,GAAQjB,CAAO;AAAA,IAAA;AAAA,EACvD;AAEJ;AAEA,SAAS8B,EACPH,GACAV,GACAjB,GACA;AACA,MAAI,CAAC2B,EAAM,QAAO,CAACV,CAAM;AACzB,MAAIc,IAAOJ,EAAK,OAAO,CAACJ,MAAQN,EAAO,OAAOM,EAAI,EAAE;AACpD,SAAAQ,EAAK,KAAKd,CAAM,GAChBc,IAAOC,EAAOD,GAAM,CAAC/B,GAAS,WAAW,IAAI,CAAC,GAC1CA,GAAS,UAAO+B,IAAOA,EAAK,MAAM,GAAG/B,EAAQ,KAAK,IAC/C+B;AACT;AAEA,eAAejB,EAAWT,GAAe;AAEvC,UADiB,MAAM4B,EAAQ5B,CAAG,GAClB,KAAK,IAAI,CAACkB,MAAQA,EAAI,KAAK,EAAE,kBAAkB,WAAA,CAAY,CAAC;AAC9E;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getCountFromServer as c } from "firebase/firestore";
|
|
2
|
+
import i from "swr";
|
|
3
|
+
import { useDb as m } from "../common/base-login.js";
|
|
4
|
+
import a from "../common/query.js";
|
|
5
|
+
const f = {
|
|
6
|
+
toFirestore: (t) => t,
|
|
7
|
+
fromFirestore: (t) => t
|
|
8
|
+
};
|
|
9
|
+
function v(t, r) {
|
|
10
|
+
const o = m(), e = a(o, t, f, r), n = {
|
|
11
|
+
revalidateOnMount: !0,
|
|
12
|
+
suspense: !0
|
|
13
|
+
}, s = `${t}?${JSON.stringify(r)}`, { data: u } = i(s, () => p(e), n);
|
|
14
|
+
return u;
|
|
15
|
+
}
|
|
16
|
+
async function p(t) {
|
|
17
|
+
return (await c(t)).data().count;
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
v as useCount
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=count.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count.js","sources":["../../../../src/web/hooks/count.ts"],"sourcesContent":["import { type FirestoreDataConverter, getCountFromServer, type Query } from \"firebase/firestore\";\nimport useSWR, { type SWRConfiguration } from \"swr\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport query, { type Constraints } from \"~/web/common/query\";\n\nconst emptyConverter: FirestoreDataConverter<any> = {\n toFirestore: (data) => data,\n fromFirestore: (data) => data,\n};\n\ntype CountOptions<T> = {\n constraints?: Constraints<T>;\n group?: boolean;\n};\n\nexport function useCount<T>(path: string, options?: CountOptions<T>) {\n const db = useDb();\n const q = query(db, path, emptyConverter, options);\n\n const swrConfig: SWRConfiguration = {\n revalidateOnMount: true,\n suspense: true,\n };\n\n const key = `${path}?${JSON.stringify(options)}`;\n const { data } = useSWR<number>(key, () => fetcher(q), swrConfig);\n\n return data!;\n}\n\nasync function fetcher<T>(ref: Query<T>) {\n const snapshot = await getCountFromServer(ref);\n return snapshot.data().count;\n}\n"],"names":["emptyConverter","data","useCount","path","options","db","useDb","q","query","swrConfig","key","useSWR","fetcher","ref","getCountFromServer"],"mappings":";;;;AAMA,MAAMA,IAA8C;AAAA,EAClD,aAAa,CAACC,MAASA;AAAA,EACvB,eAAe,CAACA,MAASA;AAC3B;AAOO,SAASC,EAAYC,GAAcC,GAA2B;AACnE,QAAMC,IAAKC,EAAA,GACLC,IAAIC,EAAMH,GAAIF,GAAMH,GAAgBI,CAAO,GAE3CK,IAA8B;AAAA,IAClC,mBAAmB;AAAA,IACnB,UAAU;AAAA,EAAA,GAGNC,IAAM,GAAGP,CAAI,IAAI,KAAK,UAAUC,CAAO,CAAC,IACxC,EAAE,MAAAH,MAASU,EAAeD,GAAK,MAAME,EAAQL,CAAC,GAAGE,CAAS;AAEhE,SAAOR;AACT;AAEA,eAAeW,EAAWC,GAAe;AAEvC,UADiB,MAAMC,EAAmBD,CAAG,GAC7B,OAAO;AACzB;"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useCallback as v } from "react";
|
|
2
|
+
import { doc as y, onSnapshot as D, getDoc as w, setDoc as O } from "firebase/firestore";
|
|
3
|
+
import { useErrorBoundary as C } from "react-error-boundary";
|
|
4
|
+
import S from "swr";
|
|
5
|
+
import { useDb as g } from "../common/base-login.js";
|
|
6
|
+
import { useSubscriptionListener as $ } from "./subscription.js";
|
|
7
|
+
const f = {
|
|
8
|
+
revalidate: !1,
|
|
9
|
+
rollbackOnError: !0,
|
|
10
|
+
populateCache: !0
|
|
11
|
+
};
|
|
12
|
+
function M(r, t, s, e) {
|
|
13
|
+
const [o, u] = k(r, t, s, e);
|
|
14
|
+
if (!o) throw new Error(`Document ${r}/${t} not found`);
|
|
15
|
+
return [o, u];
|
|
16
|
+
}
|
|
17
|
+
function k(r, t, s, e) {
|
|
18
|
+
const o = g(), { showBoundary: u } = C(), n = y(o, r, t).withConverter(s), b = {
|
|
19
|
+
revalidateIfStale: !e?.subscribe,
|
|
20
|
+
revalidateOnFocus: !e?.subscribe,
|
|
21
|
+
revalidateOnMount: !e?.subscribe,
|
|
22
|
+
revalidateOnReconnect: !e?.subscribe,
|
|
23
|
+
suspense: !0
|
|
24
|
+
}, m = `${n.path}?${JSON.stringify(e)}`, { data: d, mutate: c } = S(m, () => x(n), b);
|
|
25
|
+
$(
|
|
26
|
+
m,
|
|
27
|
+
(a) => {
|
|
28
|
+
if (!e?.subscribe) return;
|
|
29
|
+
const p = D(
|
|
30
|
+
n,
|
|
31
|
+
(i) => {
|
|
32
|
+
try {
|
|
33
|
+
a([i.data()]);
|
|
34
|
+
} catch (h) {
|
|
35
|
+
u(h);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
(i) => u(i)
|
|
39
|
+
);
|
|
40
|
+
return () => p();
|
|
41
|
+
},
|
|
42
|
+
(a) => c(a, f)
|
|
43
|
+
);
|
|
44
|
+
const l = v((a) => E(n, a, c), [c, n]);
|
|
45
|
+
return [d[0], l];
|
|
46
|
+
}
|
|
47
|
+
async function E(r, t, s) {
|
|
48
|
+
await s(
|
|
49
|
+
async () => (await O(r, t), [t]),
|
|
50
|
+
{ ...f, optimisticData: [t] }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
async function x(r) {
|
|
54
|
+
const t = await w(r);
|
|
55
|
+
return t.exists() ? [t.data({ serverTimestamps: "estimate" })] : [void 0];
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
M as useDocument,
|
|
59
|
+
k as useDocumentOptional
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sources":["../../../../src/web/hooks/document.ts"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport {\n type DocumentReference,\n doc,\n type FirestoreDataConverter,\n getDoc,\n onSnapshot,\n setDoc,\n} from \"firebase/firestore\";\nimport { useErrorBoundary } from \"react-error-boundary\";\nimport useSWR, { type KeyedMutator, type MutatorOptions, type SWRConfiguration } from \"swr\";\n\nimport { useDb } from \"~/web/common/base-login\";\n\nimport { useSubscriptionListener } from \"./subscription\";\n\nconst mutationConfig: MutatorOptions = {\n revalidate: false,\n rollbackOnError: true,\n populateCache: true,\n};\n\ntype Options = {\n subscribe?: boolean;\n};\n\nexport function useDocument<T>(\n path: string,\n id: string,\n converter: FirestoreDataConverter<T>,\n options?: Options,\n) {\n const [data, setData] = useDocumentOptional<T>(path, id, converter, options);\n if (!data) throw new Error(`Document ${path}/${id} not found`);\n return [data, setData] as const;\n}\n\nexport function useDocumentOptional<T>(\n path: string,\n id: string,\n converter: FirestoreDataConverter<T>,\n options?: Options,\n) {\n const db = useDb();\n const { showBoundary } = useErrorBoundary();\n\n const ref = doc(db, path, id).withConverter(converter);\n\n const swrConfig: SWRConfiguration = {\n revalidateIfStale: !options?.subscribe,\n revalidateOnFocus: !options?.subscribe,\n revalidateOnMount: !options?.subscribe,\n revalidateOnReconnect: !options?.subscribe,\n suspense: true,\n };\n\n const key = `${ref.path}?${JSON.stringify(options)}`;\n const { data, mutate } = useSWR<[T?]>(key, () => fetcher(ref), swrConfig);\n\n useSubscriptionListener<[T?]>(\n key,\n (setData) => {\n if (!options?.subscribe) return;\n const unsubscribe = onSnapshot(\n ref,\n (snap) => {\n try {\n setData([snap.data()]);\n } catch (err) {\n showBoundary(err);\n }\n },\n (error) => showBoundary(error),\n );\n return () => unsubscribe();\n },\n (data) => mutate(data, mutationConfig),\n );\n\n const setData = useCallback((newData: T) => setDocument<T>(ref, newData, mutate), [mutate, ref]);\n return [data![0], setData] as const;\n}\n\nasync function setDocument<T>(ref: DocumentReference<T>, newDoc: T, mutator: KeyedMutator<[T?]>) {\n await mutator(\n async () => {\n await setDoc(ref, newDoc);\n return [newDoc];\n },\n { ...mutationConfig, optimisticData: [newDoc] },\n );\n}\n\nasync function fetcher<T>(ref: DocumentReference<T>): Promise<[T?]> {\n const snapshot = await getDoc(ref);\n if (!snapshot.exists()) {\n return [undefined];\n }\n return [snapshot.data({ serverTimestamps: \"estimate\" })];\n}\n"],"names":["mutationConfig","useDocument","path","id","converter","options","data","setData","useDocumentOptional","db","useDb","showBoundary","useErrorBoundary","ref","doc","swrConfig","key","mutate","useSWR","fetcher","useSubscriptionListener","unsubscribe","onSnapshot","snap","err","error","useCallback","newData","setDocument","newDoc","mutator","setDoc","snapshot","getDoc"],"mappings":";;;;;;AAiBA,MAAMA,IAAiC;AAAA,EACrC,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,eAAe;AACjB;AAMO,SAASC,EACdC,GACAC,GACAC,GACAC,GACA;AACA,QAAM,CAACC,GAAMC,CAAO,IAAIC,EAAuBN,GAAMC,GAAIC,GAAWC,CAAO;AAC3E,MAAI,CAACC,EAAM,OAAM,IAAI,MAAM,YAAYJ,CAAI,IAAIC,CAAE,YAAY;AAC7D,SAAO,CAACG,GAAMC,CAAO;AACvB;AAEO,SAASC,EACdN,GACAC,GACAC,GACAC,GACA;AACA,QAAMI,IAAKC,EAAA,GACL,EAAE,cAAAC,EAAA,IAAiBC,EAAA,GAEnBC,IAAMC,EAAIL,GAAIP,GAAMC,CAAE,EAAE,cAAcC,CAAS,GAE/CW,IAA8B;AAAA,IAClC,mBAAmB,CAACV,GAAS;AAAA,IAC7B,mBAAmB,CAACA,GAAS;AAAA,IAC7B,mBAAmB,CAACA,GAAS;AAAA,IAC7B,uBAAuB,CAACA,GAAS;AAAA,IACjC,UAAU;AAAA,EAAA,GAGNW,IAAM,GAAGH,EAAI,IAAI,IAAI,KAAK,UAAUR,CAAO,CAAC,IAC5C,EAAE,MAAAC,GAAM,QAAAW,EAAA,IAAWC,EAAaF,GAAK,MAAMG,EAAQN,CAAG,GAAGE,CAAS;AAExE,EAAAK;AAAA,IACEJ;AAAA,IACA,CAACT,MAAY;AACX,UAAI,CAACF,GAAS,UAAW;AACzB,YAAMgB,IAAcC;AAAA,QAClBT;AAAA,QACA,CAACU,MAAS;AACR,cAAI;AACFhB,YAAAA,EAAQ,CAACgB,EAAK,KAAA,CAAM,CAAC;AAAA,UACvB,SAASC,GAAK;AACZ,YAAAb,EAAaa,CAAG;AAAA,UAClB;AAAA,QACF;AAAA,QACA,CAACC,MAAUd,EAAac,CAAK;AAAA,MAAA;AAE/B,aAAO,MAAMJ,EAAA;AAAA,IACf;AAAA,IACA,CAACf,MAASW,EAAOX,GAAMN,CAAc;AAAA,EAAA;AAGvC,QAAMO,IAAUmB,EAAY,CAACC,MAAeC,EAAef,GAAKc,GAASV,CAAM,GAAG,CAACA,GAAQJ,CAAG,CAAC;AAC/F,SAAO,CAACP,EAAM,CAAC,GAAGC,CAAO;AAC3B;AAEA,eAAeqB,EAAef,GAA2BgB,GAAWC,GAA6B;AAC/F,QAAMA;AAAA,IACJ,aACE,MAAMC,EAAOlB,GAAKgB,CAAM,GACjB,CAACA,CAAM;AAAA,IAEhB,EAAE,GAAG7B,GAAgB,gBAAgB,CAAC6B,CAAM,EAAA;AAAA,EAAE;AAElD;AAEA,eAAeV,EAAWN,GAA0C;AAClE,QAAMmB,IAAW,MAAMC,EAAOpB,CAAG;AACjC,SAAKmB,EAAS,WAGP,CAACA,EAAS,KAAK,EAAE,kBAAkB,WAAA,CAAY,CAAC,IAF9C,CAAC,MAAS;AAGrB;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState as a, useEffect as l } from "react";
|
|
2
|
+
const o = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
3
|
+
function m(s, i) {
|
|
4
|
+
const e = d(s, i);
|
|
5
|
+
if (!e.data) throw e.promise;
|
|
6
|
+
const [t, n] = a(e.data.value);
|
|
7
|
+
return l(() => (e.listeners.add(n), () => void e.listeners.delete(n)), [e]), t;
|
|
8
|
+
}
|
|
9
|
+
function g(s, i, e) {
|
|
10
|
+
const t = d(s, i);
|
|
11
|
+
l(() => (t.listeners.add(e), () => void t.listeners.delete(e)), [t, e]);
|
|
12
|
+
}
|
|
13
|
+
function d(s, i) {
|
|
14
|
+
let e = o.get(s);
|
|
15
|
+
if (!e) {
|
|
16
|
+
let t;
|
|
17
|
+
const n = new Promise((r) => {
|
|
18
|
+
t = r;
|
|
19
|
+
}), b = i((r) => {
|
|
20
|
+
t();
|
|
21
|
+
const c = o.get(s);
|
|
22
|
+
if (c) {
|
|
23
|
+
c.data = { value: r };
|
|
24
|
+
for (const p of c.listeners)
|
|
25
|
+
p(r);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
e = { promise: n, unsubscribe: b, listeners: /* @__PURE__ */ new Set() }, o.set(s, e);
|
|
29
|
+
}
|
|
30
|
+
return l(() => (clearTimeout(u.get(s)), () => {
|
|
31
|
+
clearTimeout(u.get(s));
|
|
32
|
+
const t = setTimeout(() => {
|
|
33
|
+
e?.listeners.size || (e?.unsubscribe?.(), o.delete(s), u.delete(s));
|
|
34
|
+
}, 1e3);
|
|
35
|
+
u.set(s, t);
|
|
36
|
+
}), [s, e]), e;
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
m as useSubscription,
|
|
40
|
+
g as useSubscriptionListener
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=subscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.js","sources":["../../../../src/web/hooks/subscription.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\ntype Subscription<T> = {\n promise: Promise<void>;\n unsubscribe?: () => void;\n listeners: Set<(data: T) => void>;\n data?: { value: T };\n};\n\nconst subscriptions = new Map<string, Subscription<any>>();\nconst pendingUnsubscribes = new Map<string, number>();\n\nexport function useSubscription<T>(\n key: string,\n subscribe: (set: (value: T) => void) => (() => void) | undefined,\n) {\n const subscription = useBaseSubscription(key, subscribe);\n if (!subscription.data) throw subscription.promise;\n\n const [value, setValue] = useState(subscription.data.value);\n useEffect(() => {\n subscription.listeners.add(setValue);\n return () => void subscription.listeners.delete(setValue);\n }, [subscription]);\n\n return value;\n}\n\nexport function useSubscriptionListener<T>(\n key: string,\n subscribe: (set: (value: T) => void) => (() => void) | undefined,\n onData: (value: T) => void,\n) {\n const subscription = useBaseSubscription(key, subscribe);\n\n useEffect(() => {\n subscription.listeners.add(onData);\n return () => void subscription.listeners.delete(onData);\n }, [subscription, onData]);\n}\n\nfunction useBaseSubscription<T>(\n key: string,\n subscribe: (set: (value: T) => void) => (() => void) | undefined,\n): Subscription<T> {\n let subscription = subscriptions.get(key) as Subscription<T>;\n if (!subscription) {\n let resolve!: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n\n const unsubscribe = subscribe((value) => {\n resolve();\n const sub = subscriptions.get(key);\n if (sub) {\n sub.data = { value };\n for (const listener of sub.listeners) {\n listener(value);\n }\n }\n });\n subscription = { promise, unsubscribe, listeners: new Set() };\n subscriptions.set(key, subscription);\n }\n\n useEffect(() => {\n clearTimeout(pendingUnsubscribes.get(key));\n return () => {\n clearTimeout(pendingUnsubscribes.get(key));\n const id = setTimeout(() => {\n if (subscription?.listeners.size) return;\n subscription?.unsubscribe?.();\n subscriptions.delete(key);\n pendingUnsubscribes.delete(key);\n }, 1000);\n pendingUnsubscribes.set(key, id as any);\n };\n }, [key, subscription]);\n\n return subscription;\n}\n"],"names":["subscriptions","pendingUnsubscribes","useSubscription","key","subscribe","subscription","useBaseSubscription","value","setValue","useState","useEffect","useSubscriptionListener","onData","resolve","promise","unsubscribe","sub","listener","id"],"mappings":";AASA,MAAMA,wBAAoB,IAAA,GACpBC,wBAA0B,IAAA;AAEzB,SAASC,EACdC,GACAC,GACA;AACA,QAAMC,IAAeC,EAAoBH,GAAKC,CAAS;AACvD,MAAI,CAACC,EAAa,KAAM,OAAMA,EAAa;AAE3C,QAAM,CAACE,GAAOC,CAAQ,IAAIC,EAASJ,EAAa,KAAK,KAAK;AAC1D,SAAAK,EAAU,OACRL,EAAa,UAAU,IAAIG,CAAQ,GAC5B,MAAM,KAAKH,EAAa,UAAU,OAAOG,CAAQ,IACvD,CAACH,CAAY,CAAC,GAEVE;AACT;AAEO,SAASI,EACdR,GACAC,GACAQ,GACA;AACA,QAAMP,IAAeC,EAAoBH,GAAKC,CAAS;AAEvD,EAAAM,EAAU,OACRL,EAAa,UAAU,IAAIO,CAAM,GAC1B,MAAM,KAAKP,EAAa,UAAU,OAAOO,CAAM,IACrD,CAACP,GAAcO,CAAM,CAAC;AAC3B;AAEA,SAASN,EACPH,GACAC,GACiB;AACjB,MAAIC,IAAeL,EAAc,IAAIG,CAAG;AACxC,MAAI,CAACE,GAAc;AACjB,QAAIQ;AACJ,UAAMC,IAAU,IAAI,QAAc,CAAC,MAAM;AACvC,MAAAD,IAAU;AAAA,IACZ,CAAC,GAEKE,IAAcX,EAAU,CAACG,MAAU;AACvC,MAAAM,EAAA;AACA,YAAMG,IAAMhB,EAAc,IAAIG,CAAG;AACjC,UAAIa,GAAK;AACP,QAAAA,EAAI,OAAO,EAAE,OAAAT,EAAA;AACb,mBAAWU,KAAYD,EAAI;AACzB,UAAAC,EAASV,CAAK;AAAA,MAElB;AAAA,IACF,CAAC;AACD,IAAAF,IAAe,EAAE,SAAAS,GAAS,aAAAC,GAAa,WAAW,oBAAI,MAAI,GAC1Df,EAAc,IAAIG,GAAKE,CAAY;AAAA,EACrC;AAEA,SAAAK,EAAU,OACR,aAAaT,EAAoB,IAAIE,CAAG,CAAC,GAClC,MAAM;AACX,iBAAaF,EAAoB,IAAIE,CAAG,CAAC;AACzC,UAAMe,IAAK,WAAW,MAAM;AAC1B,MAAIb,GAAc,UAAU,SAC5BA,GAAc,cAAA,GACdL,EAAc,OAAOG,CAAG,GACxBF,EAAoB,OAAOE,CAAG;AAAA,IAChC,GAAG,GAAI;AACP,IAAAF,EAAoB,IAAIE,GAAKe,CAAS;AAAA,EACxC,IACC,CAACf,GAAKE,CAAY,CAAC,GAEfA;AACT;"}
|
package/dist/vite.d.ts
ADDED
package/dist/vite.js
ADDED
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","sources":["../src/vite/index.ts"],"sourcesContent":["import firebaseEntry from \"./entry\";\nimport firebasePreset from \"./preset\";\n\nexport default [firebaseEntry(), firebasePreset()] as any;\n"],"names":["index","firebaseEntry","firebasePreset"],"mappings":";;AAGA,MAAAA,IAAe,CAACC,EAAA,GAAiBC,GAAgB;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@olinfo/quizms-firebase",
|
|
3
|
+
"version": "4.0.0-canary.20251130",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"prebuild": "biome check && tsc --noEmit",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"watch": "vite build --watch",
|
|
11
|
+
"lint": "biome check --write && tsc --noEmit",
|
|
12
|
+
"prepack": "pnpm build"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"quizms-firebase": "bin/bin.js"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
"./entry": {
|
|
19
|
+
"types": "./dist/entry.d.ts",
|
|
20
|
+
"default": "./dist/entry.js"
|
|
21
|
+
},
|
|
22
|
+
"./functions": {
|
|
23
|
+
"types": "./dist/functions.d.ts",
|
|
24
|
+
"default": "./dist/functions.js"
|
|
25
|
+
},
|
|
26
|
+
"./vite": {
|
|
27
|
+
"types": "./dist/vite.d.ts",
|
|
28
|
+
"default": "./dist/vite.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin",
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@inquirer/prompts": "^7.10.1",
|
|
37
|
+
"@noble/hashes": "^1.8.0",
|
|
38
|
+
"@olinfo/quizms": "workspace:^",
|
|
39
|
+
"@olinfo/react-components": "^0.12.0",
|
|
40
|
+
"@types/cli-progress": "^3.11.6",
|
|
41
|
+
"@types/lodash-es": "^4.17.12",
|
|
42
|
+
"@types/picomatch": "^4.0.2",
|
|
43
|
+
"ag-grid-community": "^33.3.2",
|
|
44
|
+
"cli-progress": "^3.12.0",
|
|
45
|
+
"clsx": "^2.1.1",
|
|
46
|
+
"commander": "^14.0.2",
|
|
47
|
+
"date-fns": "^4.1.0",
|
|
48
|
+
"fastify": "^5.6.2",
|
|
49
|
+
"firebase": "^12.6.0",
|
|
50
|
+
"firebase-admin": "^13.6.0",
|
|
51
|
+
"firebase-functions": "^7.0.0",
|
|
52
|
+
"lodash-es": "^4.17.21",
|
|
53
|
+
"lucide-react": "^0.553.0",
|
|
54
|
+
"picocolors": "^1.1.1",
|
|
55
|
+
"picomatch": "^4.0.3",
|
|
56
|
+
"react-error-boundary": "^5.0.0",
|
|
57
|
+
"react-markdown": "^10.1.0",
|
|
58
|
+
"rollup-plugin-node-externals": "^8.1.2",
|
|
59
|
+
"rollup-preserve-directives": "^1.1.3",
|
|
60
|
+
"swr": "^2.3.7",
|
|
61
|
+
"tinyglobby": "^0.2.15",
|
|
62
|
+
"vite": "^7.2.4",
|
|
63
|
+
"vite-plugin-dts": "^4.5.4",
|
|
64
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
65
|
+
"wouter": "^3.8.0",
|
|
66
|
+
"zod": "^3.25.76"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"react": "^19"
|
|
70
|
+
},
|
|
71
|
+
"postcss": {
|
|
72
|
+
"plugins": {
|
|
73
|
+
"tailwindcss": {}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@biomejs/biome": "^2.3.8",
|
|
78
|
+
"@google-cloud/storage": "^7.17.3",
|
|
79
|
+
"@types/react": "^19.2.7",
|
|
80
|
+
"@types/wicg-file-system-access": "^2023.10.7",
|
|
81
|
+
"typescript": "5.8.2"
|
|
82
|
+
}
|
|
83
|
+
}
|