@olinfo/quizms-firebase 4.0.1 → 4.1.0

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.
Files changed (65) hide show
  1. package/dist/functions.d.ts +2 -11
  2. package/dist/functions.js +2 -2
  3. package/dist/package.json.js +1 -1
  4. package/dist/src/cli/files/functions-index.js +4 -4
  5. package/dist/src/cli/files/functions-index.js.map +1 -1
  6. package/dist/src/cli/init.js +8 -8
  7. package/dist/src/cli/init.js.map +1 -1
  8. package/dist/src/cli/update-scores.js +21 -32
  9. package/dist/src/cli/update-scores.js.map +1 -1
  10. package/dist/src/cli/utils/collection.js +20 -18
  11. package/dist/src/cli/utils/collection.js.map +1 -1
  12. package/dist/src/cli/utils/converters-admin.js +31 -32
  13. package/dist/src/cli/utils/converters-admin.js.map +1 -1
  14. package/dist/src/cli/utils/region-finder.js +6 -5
  15. package/dist/src/cli/utils/region-finder.js.map +1 -1
  16. package/dist/src/cli/utils/storage.js +26 -35
  17. package/dist/src/cli/utils/storage.js.map +1 -1
  18. package/dist/src/cli/utils/users.js +13 -12
  19. package/dist/src/cli/utils/users.js.map +1 -1
  20. package/dist/src/functions/api/admin.js +25 -10
  21. package/dist/src/functions/api/admin.js.map +1 -1
  22. package/dist/src/functions/api/index.js +26 -37
  23. package/dist/src/functions/api/index.js.map +1 -1
  24. package/dist/src/functions/api/queries.js +5 -5
  25. package/dist/src/functions/api/student.js +62 -72
  26. package/dist/src/functions/api/student.js.map +1 -1
  27. package/dist/src/functions/api/teacher.js +104 -111
  28. package/dist/src/functions/api/teacher.js.map +1 -1
  29. package/dist/src/functions/api/trpc.js +18 -0
  30. package/dist/src/functions/api/trpc.js.map +1 -0
  31. package/dist/src/functions/submissions.js +1 -1
  32. package/dist/src/functions/submissions.js.map +1 -1
  33. package/dist/src/models/user.js +2 -2
  34. package/dist/src/models/user.js.map +1 -1
  35. package/dist/src/models/website.js +1 -1
  36. package/dist/src/models/website.js.map +1 -1
  37. package/dist/src/vite/preset.js +3 -4
  38. package/dist/src/vite/preset.js.map +1 -1
  39. package/dist/src/web/admin/announcements.js +9 -10
  40. package/dist/src/web/admin/announcements.js.map +1 -1
  41. package/dist/src/web/admin/school-table.js +7 -8
  42. package/dist/src/web/admin/school-table.js.map +1 -1
  43. package/dist/src/web/common/api.js +32 -35
  44. package/dist/src/web/common/api.js.map +1 -1
  45. package/dist/src/web/common/converters.js +35 -36
  46. package/dist/src/web/common/converters.js.map +1 -1
  47. package/dist/src/web/common/token-login.js +11 -11
  48. package/dist/src/web/common/token-login.js.map +1 -1
  49. package/dist/src/web/entry/admin.js +5 -7
  50. package/dist/src/web/entry/admin.js.map +1 -1
  51. package/dist/src/web/entry/student-restoring.js +18 -20
  52. package/dist/src/web/entry/student-restoring.js.map +1 -1
  53. package/dist/src/web/entry/student.js +70 -76
  54. package/dist/src/web/entry/student.js.map +1 -1
  55. package/dist/src/web/entry/teacher.js +68 -68
  56. package/dist/src/web/entry/teacher.js.map +1 -1
  57. package/dist/src/web/hooks/collection.js +29 -30
  58. package/dist/src/web/hooks/collection.js.map +1 -1
  59. package/dist/src/web/hooks/document.js +35 -36
  60. package/dist/src/web/hooks/document.js.map +1 -1
  61. package/dist/src/web/hooks/subscription.js +26 -22
  62. package/dist/src/web/hooks/subscription.js.map +1 -1
  63. package/package.json +23 -19
  64. package/dist/src/functions/api/schema.js +0 -42
  65. package/dist/src/functions/api/schema.js.map +0 -1
@@ -1,61 +1,60 @@
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 = {
1
+ import { useCallback as h } from "react";
2
+ import { doc as v, onSnapshot as D, getDoc as b, setDoc as w } from "firebase/firestore";
3
+ import { useErrorBoundary as y } from "react-error-boundary";
4
+ import C from "swr";
5
+ import { useDb as O } from "../common/base-login.js";
6
+ import { useSubscriptionListener as S } from "./subscription.js";
7
+ const c = {
8
8
  revalidate: !1,
9
9
  rollbackOnError: !0,
10
10
  populateCache: !0
11
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];
12
+ function L(e, t, o) {
13
+ const [n, s] = g(e, t, o);
14
+ if (!n) throw new Error(`Document ${e}/${t} not found`);
15
+ return [n, s];
16
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,
17
+ function g(e, t, o) {
18
+ const n = O(), { showBoundary: s } = y(), r = v(n, e, t).withConverter(o), f = {
19
+ revalidateIfStale: !1,
20
+ revalidateOnFocus: !1,
21
+ revalidateOnMount: !1,
22
+ revalidateOnReconnect: !1,
23
23
  suspense: !0
24
- }, m = `${n.path}?${JSON.stringify(e)}`, { data: d, mutate: c } = S(m, () => x(n), b);
25
- $(
26
- m,
24
+ }, { data: m, mutate: u } = C(r.path, () => k(r), f);
25
+ S(
26
+ r.path,
27
27
  (a) => {
28
- if (!e?.subscribe) return;
29
28
  const p = D(
30
- n,
29
+ r,
31
30
  (i) => {
32
31
  try {
33
32
  a([i.data()]);
34
- } catch (h) {
35
- u(h);
33
+ } catch (d) {
34
+ s(d);
36
35
  }
37
36
  },
38
- (i) => u(i)
37
+ (i) => s(i)
39
38
  );
40
39
  return () => p();
41
40
  },
42
- (a) => c(a, f)
41
+ (a) => u(a, c)
43
42
  );
44
- const l = v((a) => E(n, a, c), [c, n]);
45
- return [d[0], l];
43
+ const l = h((a) => E(r, a, u), [u, r]);
44
+ return [m[0], l];
46
45
  }
47
- async function E(r, t, s) {
48
- await s(
49
- async () => (await O(r, t), [t]),
50
- { ...f, optimisticData: [t] }
46
+ async function E(e, t, o) {
47
+ await o(
48
+ async () => (await w(e, t), [t]),
49
+ { ...c, optimisticData: [t] }
51
50
  );
52
51
  }
53
- async function x(r) {
54
- const t = await w(r);
52
+ async function k(e) {
53
+ const t = await b(e);
55
54
  return t.exists() ? [t.data({ serverTimestamps: "estimate" })] : [void 0];
56
55
  }
57
56
  export {
58
- M as useDocument,
59
- k as useDocumentOptional
57
+ L as useDocument,
58
+ g as useDocumentOptional
60
59
  };
61
60
  //# sourceMappingURL=document.js.map
@@ -1 +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;"}
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\nexport function useDocument<T>(path: string, id: string, converter: FirestoreDataConverter<T>) {\n const [data, setData] = useDocumentOptional<T>(path, id, converter);\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) {\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: false,\n revalidateOnFocus: false,\n revalidateOnMount: false,\n revalidateOnReconnect: false,\n suspense: true,\n };\n\n const { data, mutate } = useSWR<[T?]>(ref.path, () => fetcher(ref), swrConfig);\n\n useSubscriptionListener<[T?]>(\n ref.path,\n (setData) => {\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","data","setData","useDocumentOptional","db","useDb","showBoundary","useErrorBoundary","ref","doc","swrConfig","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;AAEO,SAASC,EAAeC,GAAcC,GAAYC,GAAsC;AAC7F,QAAM,CAACC,GAAMC,CAAO,IAAIC,EAAuBL,GAAMC,GAAIC,CAAS;AAClE,MAAI,CAACC,EAAM,OAAM,IAAI,MAAM,YAAYH,CAAI,IAAIC,CAAE,YAAY;AAC7D,SAAO,CAACE,GAAMC,CAAO;AACvB;AAEO,SAASC,EACdL,GACAC,GACAC,GACA;AACA,QAAMI,IAAKC,EAAA,GACL,EAAE,cAAAC,EAAA,IAAiBC,EAAA,GAEnBC,IAAMC,EAAIL,GAAIN,GAAMC,CAAE,EAAE,cAAcC,CAAS,GAE/CU,IAA8B;AAAA,IAClC,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,UAAU;AAAA,EAAA,GAGN,EAAE,MAAAT,GAAM,QAAAU,EAAA,IAAWC,EAAaJ,EAAI,MAAM,MAAMK,EAAQL,CAAG,GAAGE,CAAS;AAE7E,EAAAI;AAAA,IACEN,EAAI;AAAA,IACJ,CAACN,MAAY;AACX,YAAMa,IAAcC;AAAA,QAClBR;AAAA,QACA,CAACS,MAAS;AACR,cAAI;AACFf,YAAAA,EAAQ,CAACe,EAAK,KAAA,CAAM,CAAC;AAAA,UACvB,SAASC,GAAK;AACZ,YAAAZ,EAAaY,CAAG;AAAA,UAClB;AAAA,QACF;AAAA,QACA,CAACC,MAAUb,EAAaa,CAAK;AAAA,MAAA;AAE/B,aAAO,MAAMJ,EAAA;AAAA,IACf;AAAA,IACA,CAACd,MAASU,EAAOV,GAAML,CAAc;AAAA,EAAA;AAGvC,QAAMM,IAAUkB,EAAY,CAACC,MAAeC,EAAed,GAAKa,GAASV,CAAM,GAAG,CAACA,GAAQH,CAAG,CAAC;AAC/F,SAAO,CAACP,EAAM,CAAC,GAAGC,CAAO;AAC3B;AAEA,eAAeoB,EAAed,GAA2Be,GAAWC,GAA6B;AAC/F,QAAMA;AAAA,IACJ,aACE,MAAMC,EAAOjB,GAAKe,CAAM,GACjB,CAACA,CAAM;AAAA,IAEhB,EAAE,GAAG3B,GAAgB,gBAAgB,CAAC2B,CAAM,EAAA;AAAA,EAAE;AAElD;AAEA,eAAeV,EAAWL,GAA0C;AAClE,QAAMkB,IAAW,MAAMC,EAAOnB,CAAG;AACjC,SAAKkB,EAAS,WAGP,CAACA,EAAS,KAAK,EAAE,kBAAkB,WAAA,CAAY,CAAC,IAF9C,CAAC,MAAS;AAGrB;"}
@@ -1,42 +1,46 @@
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;
1
+ import { use as f, useState as a, useEffect as l } from "react";
2
+ const u = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
3
+ function g(s, n) {
4
+ const e = b(s, n);
5
+ f(e.promise);
6
+ const [t, i] = a(e.data.value);
7
+ return l(() => (e.listeners.add(i), () => {
8
+ e.listeners.delete(i);
9
+ }), [e]), t;
8
10
  }
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]);
11
+ function S(s, n, e) {
12
+ const t = b(s, n);
13
+ l(() => (t.listeners.add(e), () => {
14
+ t.listeners.delete(e);
15
+ }), [t, e]);
12
16
  }
13
- function d(s, i) {
14
- let e = o.get(s);
17
+ function b(s, n) {
18
+ let e = u.get(s);
15
19
  if (!e) {
16
20
  let t;
17
- const n = new Promise((r) => {
21
+ const i = new Promise((r) => {
18
22
  t = r;
19
- }), b = i((r) => {
23
+ }), d = n((r) => {
20
24
  t();
21
- const c = o.get(s);
25
+ const c = u.get(s);
22
26
  if (c) {
23
27
  c.data = { value: r };
24
28
  for (const p of c.listeners)
25
29
  p(r);
26
30
  }
27
31
  });
28
- e = { promise: n, unsubscribe: b, listeners: /* @__PURE__ */ new Set() }, o.set(s, e);
32
+ e = { promise: i, unsubscribe: d, listeners: /* @__PURE__ */ new Set() }, u.set(s, e);
29
33
  }
30
- return l(() => (clearTimeout(u.get(s)), () => {
31
- clearTimeout(u.get(s));
34
+ return l(() => (clearTimeout(o.get(s)), () => {
35
+ clearTimeout(o.get(s));
32
36
  const t = setTimeout(() => {
33
- e?.listeners.size || (e?.unsubscribe?.(), o.delete(s), u.delete(s));
37
+ e?.listeners.size || (e?.unsubscribe?.(), u.delete(s), o.delete(s));
34
38
  }, 1e3);
35
- u.set(s, t);
39
+ o.set(s, t);
36
40
  }), [s, e]), e;
37
41
  }
38
42
  export {
39
- m as useSubscription,
40
- g as useSubscriptionListener
43
+ g as useSubscription,
44
+ S as useSubscriptionListener
41
45
  };
42
46
  //# sourceMappingURL=subscription.js.map
@@ -1 +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;"}
1
+ {"version":3,"file":"subscription.js","sources":["../../../../src/web/hooks/subscription.ts"],"sourcesContent":["import { use, 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 use(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","use","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,EAAAG,EAAIF,EAAa,OAAO;AAExB,QAAM,CAACG,GAAOC,CAAQ,IAAIC,EAASL,EAAa,KAAM,KAAK;AAC3D,SAAAM,EAAU,OACRN,EAAa,UAAU,IAAII,CAAQ,GAC5B,MAAA;AAAM,IAAKJ,EAAa,UAAU,OAAOI,CAAQ;AAAA,MACvD,CAACJ,CAAY,CAAC,GAEVG;AACT;AAEO,SAASI,EACdT,GACAC,GACAS,GACA;AACA,QAAMR,IAAeC,EAAoBH,GAAKC,CAAS;AAEvD,EAAAO,EAAU,OACRN,EAAa,UAAU,IAAIQ,CAAM,GAC1B,MAAA;AAAM,IAAKR,EAAa,UAAU,OAAOQ,CAAM;AAAA,MACrD,CAACR,GAAcQ,CAAM,CAAC;AAC3B;AAEA,SAASP,EACPH,GACAC,GACiB;AACjB,MAAIC,IAAeL,EAAc,IAAIG,CAAG;AACxC,MAAI,CAACE,GAAc;AACjB,QAAIS;AACJ,UAAMC,IAAU,IAAI,QAAc,CAAC,MAAM;AACvC,MAAAD,IAAU;AAAA,IACZ,CAAC,GAEKE,IAAcZ,EAAU,CAACI,MAAU;AACvC,MAAAM,EAAA;AACA,YAAMG,IAAMjB,EAAc,IAAIG,CAAG;AACjC,UAAIc,GAAK;AACP,QAAAA,EAAI,OAAO,EAAE,OAAAT,EAAA;AACb,mBAAWU,KAAYD,EAAI;AACzB,UAAAC,EAASV,CAAK;AAAA,MAElB;AAAA,IACF,CAAC;AACD,IAAAH,IAAe,EAAE,SAAAU,GAAS,aAAAC,GAAa,WAAW,oBAAI,MAAI,GAC1DhB,EAAc,IAAIG,GAAKE,CAAY;AAAA,EACrC;AAEA,SAAAM,EAAU,OACR,aAAaV,EAAoB,IAAIE,CAAG,CAAC,GAClC,MAAM;AACX,iBAAaF,EAAoB,IAAIE,CAAG,CAAC;AACzC,UAAMgB,IAAK,WAAW,MAAM;AAC1B,MAAId,GAAc,UAAU,SAC5BA,GAAc,cAAA,GACdL,EAAc,OAAOG,CAAG,GACxBF,EAAoB,OAAOE,CAAG;AAAA,IAChC,GAAG,GAAI;AACP,IAAAF,EAAoB,IAAIE,GAAKgB,CAAS;AAAA,EACxC,IACC,CAAChB,GAAKE,CAAY,CAAC,GAEfA;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olinfo/quizms-firebase",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "type": "module",
5
5
  "author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
6
6
  "license": "MIT",
@@ -26,40 +26,43 @@
26
26
  "dist"
27
27
  ],
28
28
  "dependencies": {
29
- "@inquirer/prompts": "^7.10.1",
30
- "@noble/hashes": "^1.8.0",
29
+ "@inquirer/prompts": "^8.1.0",
31
30
  "@olinfo/react-components": "^0.12.0",
31
+ "@trpc/client": "^11.8.0",
32
+ "@trpc/server": "^11.8.0",
32
33
  "@types/cli-progress": "^3.11.6",
34
+ "@types/express": "^5.0.6",
35
+ "@types/express-serve-static-core": "^5.1.0",
33
36
  "@types/lodash-es": "^4.17.12",
34
37
  "@types/picomatch": "^4.0.2",
35
- "ag-grid-community": "^33.3.2",
38
+ "ag-grid-community": "^35.0.0",
36
39
  "cli-progress": "^3.12.0",
37
40
  "clsx": "^2.1.1",
38
41
  "commander": "^14.0.2",
39
42
  "date-fns": "^4.1.0",
43
+ "express": "^5.2.1",
40
44
  "fastify": "^5.6.2",
41
- "firebase": "^12.6.0",
45
+ "firebase": "^12.7.0",
42
46
  "firebase-admin": "^13.6.0",
43
- "firebase-functions": "^7.0.0",
44
- "lodash-es": "^4.17.21",
45
- "lucide-react": "^0.553.0",
46
- "picocolors": "^1.1.1",
47
+ "firebase-functions": "^7.0.2",
48
+ "lodash-es": "^4.17.22",
49
+ "lucide-react": "^0.562.0",
47
50
  "picomatch": "^4.0.3",
48
- "react-error-boundary": "^5.0.0",
51
+ "react-error-boundary": "^6.0.0",
49
52
  "react-markdown": "^10.1.0",
50
53
  "rollup-plugin-node-externals": "^8.1.2",
51
54
  "rollup-preserve-directives": "^1.1.3",
52
- "swr": "^2.3.7",
55
+ "superjson": "^2.2.6",
56
+ "swr": "^2.3.8",
53
57
  "tinyglobby": "^0.2.15",
54
- "vite": "^7.2.6",
58
+ "vite": "^7.3.0",
55
59
  "vite-plugin-dts": "^4.5.4",
56
- "vite-tsconfig-paths": "^5.1.4",
57
- "wouter": "^3.8.1",
58
- "zod": "^3.25.76",
59
- "@olinfo/quizms": "^4.0.0"
60
+ "wouter": "^3.9.0",
61
+ "zod": "^4.2.1",
62
+ "@olinfo/quizms": "^4.1.0"
60
63
  },
61
64
  "peerDependencies": {
62
- "react": "~19.1"
65
+ "react": "~19.1.4"
63
66
  },
64
67
  "postcss": {
65
68
  "plugins": {
@@ -67,11 +70,12 @@
67
70
  }
68
71
  },
69
72
  "devDependencies": {
70
- "@biomejs/biome": "^2.3.8",
73
+ "@biomejs/biome": "^2.3.10",
71
74
  "@google-cloud/storage": "^7.18.0",
72
75
  "@types/react": "^19.2.7",
73
76
  "@types/wicg-file-system-access": "^2023.10.7",
74
- "typescript": "5.8.2"
77
+ "typescript": "5.8.2",
78
+ "vite-tsconfig-paths": "^6.0.3"
75
79
  },
76
80
  "scripts": {
77
81
  "prebuild": "biome check && tsc --noEmit",
@@ -1,42 +0,0 @@
1
- import t from "zod";
2
- const i = t.object({
3
- action: t.literal("studentLogin"),
4
- contestId: t.string(),
5
- token: t.string(),
6
- userData: t.record(t.coerce.string()),
7
- extraData: t.record(t.any())
8
- }), a = t.object({
9
- action: t.literal("adminLogin"),
10
- username: t.string(),
11
- password: t.string()
12
- }), e = t.object({
13
- action: t.literal("teacherLogin"),
14
- username: t.string(),
15
- password: t.string()
16
- }), n = t.object({
17
- action: t.literal("teacherStartParticipation"),
18
- participationId: t.string()
19
- }), c = t.object({
20
- action: t.literal("teacherStopParticipation"),
21
- participationId: t.string()
22
- }), r = t.object({
23
- action: t.literal("teacherFinalizeParticipation"),
24
- participationId: t.string()
25
- }), s = t.discriminatedUnion("action", [
26
- a,
27
- i,
28
- e,
29
- n,
30
- c,
31
- r
32
- ]);
33
- export {
34
- a as adminLoginSchema,
35
- s as apiRequestSchema,
36
- i as studentLoginSchema,
37
- r as teacherFinalizeParticipationSchema,
38
- e as teacherLoginSchema,
39
- n as teacherStartParticipationSchema,
40
- c as teacherStopParticipationSchema
41
- };
42
- //# sourceMappingURL=schema.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema.js","sources":["../../../../src/functions/api/schema.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const studentLoginSchema = z.object({\n action: z.literal(\"studentLogin\"),\n contestId: z.string(),\n token: z.string(),\n userData: z.record(z.coerce.string()),\n extraData: z.record(z.any()),\n});\nexport type StudentLogin = z.infer<typeof studentLoginSchema>;\n\nexport const adminLoginSchema = z.object({\n action: z.literal(\"adminLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type AdminLogin = z.infer<typeof adminLoginSchema>;\n\nexport const teacherLoginSchema = z.object({\n action: z.literal(\"teacherLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type TeacherLogin = z.infer<typeof teacherLoginSchema>;\n\nexport const teacherStartParticipationSchema = z.object({\n action: z.literal(\"teacherStartParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStartParticipation = z.infer<typeof teacherStartParticipationSchema>;\n\nexport const teacherStopParticipationSchema = z.object({\n action: z.literal(\"teacherStopParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStopParticipation = z.infer<typeof teacherStopParticipationSchema>;\n\nexport const teacherFinalizeParticipationSchema = z.object({\n action: z.literal(\"teacherFinalizeParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherFinalizeParticipation = z.infer<typeof teacherFinalizeParticipationSchema>;\n\nexport const apiRequestSchema = z.discriminatedUnion(\"action\", [\n adminLoginSchema,\n studentLoginSchema,\n teacherLoginSchema,\n teacherStartParticipationSchema,\n teacherStopParticipationSchema,\n teacherFinalizeParticipationSchema,\n]);\n\nexport type ApiRequest = z.infer<typeof apiRequestSchema>;\n"],"names":["studentLoginSchema","z","adminLoginSchema","teacherLoginSchema","teacherStartParticipationSchema","teacherStopParticipationSchema","teacherFinalizeParticipationSchema","apiRequestSchema"],"mappings":";AAEO,MAAMA,IAAqBC,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,WAAWA,EAAE,OAAA;AAAA,EACb,OAAOA,EAAE,OAAA;AAAA,EACT,UAAUA,EAAE,OAAOA,EAAE,OAAO,QAAQ;AAAA,EACpC,WAAWA,EAAE,OAAOA,EAAE,KAAK;AAC7B,CAAC,GAGYC,IAAmBD,EAAE,OAAO;AAAA,EACvC,QAAQA,EAAE,QAAQ,YAAY;AAAA,EAC9B,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC,GAGYE,IAAqBF,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC,GAGYG,IAAkCH,EAAE,OAAO;AAAA,EACtD,QAAQA,EAAE,QAAQ,2BAA2B;AAAA,EAC7C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYI,IAAiCJ,EAAE,OAAO;AAAA,EACrD,QAAQA,EAAE,QAAQ,0BAA0B;AAAA,EAC5C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYK,IAAqCL,EAAE,OAAO;AAAA,EACzD,QAAQA,EAAE,QAAQ,8BAA8B;AAAA,EAChD,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYM,IAAmBN,EAAE,mBAAmB,UAAU;AAAA,EAC7DC;AAAA,EACAF;AAAA,EACAG;AAAA,EACAC;AAAA,EACAC;AAAA,EACAC;AACF,CAAC;"}