@olinfo/quizms-firebase 4.1.0 → 4.1.2

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.
@@ -1,4 +1,4 @@
1
- const e = "4.1.0", n = { "firebase-functions": "^7.0.2" }, s = {
1
+ const e = "4.1.2", n = { "firebase-functions": "^7.0.5" }, s = {
2
2
  version: e,
3
3
  dependencies: n
4
4
  };
@@ -1,23 +1,23 @@
1
- import { existsSync as C } from "node:fs";
2
- import { readFile as j } from "node:fs/promises";
3
- import p from "node:path";
4
- import { contestSchema as g, participationSchema as M, variantsConfigSchema as y, studentSchema as V, variantSchema as P } from "@olinfo/quizms/models";
5
- import { Rng as x, validate as z } from "@olinfo/quizms/utils";
6
- import { fatal as u, warning as D, success as O, load as d } from "@olinfo/quizms/utils-node";
7
- import { deleteApp as U } from "firebase-admin/app";
8
- import { groupBy as A, uniq as I } from "lodash-es";
9
- import N from "picomatch";
10
- import { glob as k } from "tinyglobby";
11
- import w from "zod";
12
- import { userSchema as q } from "../models/user.js";
13
- import { websiteSchema as F } from "../models/website.js";
14
- import { importCollection as h } from "./utils/collection.js";
15
- import { contestConverter as Q, participationConverter as B, studentConverter as E, variantConverter as J, websiteConverter as R } from "./utils/converters-admin.js";
16
- import { initializeFirebase as W } from "./utils/initialize.js";
17
- import { importStorage as Z } from "./utils/storage.js";
18
- import { importUsers as b } from "./utils/users.js";
19
- async function vt(t) {
20
- if (process.env.QUIZMS_MODE = "contest", C("data") || u("Cannot find data directory. Make sure you're inside a QuizMS project."), [
1
+ import { existsSync as I } from "node:fs";
2
+ import { readFile as b } from "node:fs/promises";
3
+ import m from "node:path";
4
+ import { contestSchema as $, participationSchema as g, studentSchema as C, variantsConfigSchema as j, variantSchema as V } from "@olinfo/quizms/models";
5
+ import { Rng as M, validate as P } from "@olinfo/quizms/utils";
6
+ import { fatal as u, warning as x, success as z, load as f, loadContests as v } from "@olinfo/quizms/utils-node";
7
+ import { deleteApp as D } from "firebase-admin/app";
8
+ import { groupBy as O, uniq as y } from "lodash-es";
9
+ import U from "picomatch";
10
+ import { glob as A } from "tinyglobby";
11
+ import l from "zod";
12
+ import { userSchema as N } from "../models/user.js";
13
+ import { websiteSchema as k } from "../models/website.js";
14
+ import { importCollection as w } from "./utils/collection.js";
15
+ import { contestConverter as q, participationConverter as F, studentConverter as Q, variantConverter as B, websiteConverter as E } from "./utils/converters-admin.js";
16
+ import { initializeFirebase as J } from "./utils/initialize.js";
17
+ import { importStorage as R } from "./utils/storage.js";
18
+ import { importUsers as S } from "./utils/users.js";
19
+ async function ht(t) {
20
+ if (process.env.QUIZMS_MODE = "contest", I("data") || u("Cannot find data directory. Make sure you're inside a QuizMS project."), [
21
21
  "admins",
22
22
  "contests",
23
23
  "schools",
@@ -26,126 +26,115 @@ async function vt(t) {
26
26
  "teachers",
27
27
  "variants",
28
28
  "websites"
29
- ].every((m) => !t[m])) {
30
- D("`Nothing to import. Use `--help` for usage.`");
29
+ ].every((n) => !t[n])) {
30
+ x("`Nothing to import. Use `--help` for usage.`");
31
31
  return;
32
32
  }
33
- const { app: n, bucket: c, db: s } = await W();
34
- t.admins && await _(s, t), t.contests && await G(s, t), (t.schools || t.teachers) && await H(s, t), t.students && await K(s, t), t.variants && await L(s, t), t.websites && await T(s, t), t.statements && await X(c, t), O("All done!"), await U(n);
33
+ const { app: e, bucket: o, db: i } = await J();
34
+ t.admins && await W(i, t), t.contests && await Z(i, t), (t.schools || t.teachers) && await _(i, t), t.students && await G(i, t), t.variants && await H(i, t), t.websites && await K(i, t), t.statements && await L(o, t), z("All done!"), await D(e);
35
35
  }
36
- async function _(t, i) {
37
- const n = await d("admins", q.omit({ role: !0 }));
38
- await b(t, "admin", n, i);
36
+ async function W(t, s) {
37
+ const e = await f("admins", N.omit({ role: !0 }));
38
+ await S(t, "admin", e, s);
39
39
  }
40
- async function G(t, i) {
41
- const n = await d("contests", g);
42
- await h(t, "contests", n, Q, i);
40
+ async function Z(t, s) {
41
+ const e = await f("contests", $);
42
+ await w(t, "contests", e, q, s);
43
43
  }
44
- async function H(t, i) {
45
- const n = M.omit({
44
+ async function _(t, s) {
45
+ const e = g.omit({
46
46
  schoolId: !0,
47
47
  contestId: !0
48
48
  }).extend({
49
- contestIds: w.union([w.string(), w.array(w.string())]).default("*"),
50
- password: w.string()
51
- }), c = await d("schools", n), s = await d("contests", g);
52
- let m;
53
- if (i.teachers) {
54
- const r = c.map((a) => ({
49
+ contestIds: l.union([l.string(), l.array(l.string())]).default("*"),
50
+ password: l.string()
51
+ }), o = await f("schools", e), i = await v();
52
+ if (s.teachers) {
53
+ const n = o.map((a) => ({
55
54
  name: a.name,
56
55
  username: a.id,
57
56
  password: a.password
58
57
  }));
59
- await b(t, "teacher", r, i);
58
+ await S(t, "teacher", n, s);
60
59
  }
61
- if (i.schools) {
62
- const r = [];
63
- for (const a of s)
64
- for (const e of c) {
65
- if (!N.isMatch(a.id, e.contestIds)) continue;
66
- let f;
67
- if (a.hasVariants)
68
- if (e.pdfVariants)
69
- f = e.pdfVariants.map((o) => `${a.id}-${o}`);
70
- else {
71
- m || (m = await d("variants", y));
72
- const o = m.find((S) => S.id === a.id);
73
- o || u(`Missing variants configuration for contest ${a.id}.`), f = new x(`${o.secret}-${o.id}-${e.id}-participation`).sample(o.pdfVariantIds, o.pdfPerSchool);
74
- }
75
- r.push({
76
- id: `${e.id}-${a.id}`,
77
- schoolId: e.id,
60
+ if (s.schools) {
61
+ const n = [];
62
+ for (const a of i)
63
+ for (const r of o) {
64
+ if (!U.isMatch(a.id, r.contestIds)) continue;
65
+ let c;
66
+ a.hasVariants && (r.pdfVariants ? c = r.pdfVariants.map((d) => `${a.id}-${d}`) : c = new M(`${a.secret}-${a.id}-${r.id}-participation`).sample(a.pdfVariantIds, a.pdfPerSchool)), n.push({
67
+ id: `${r.id}-${a.id}`,
68
+ schoolId: r.id,
78
69
  contestId: a.id,
79
- name: e.name,
70
+ name: r.name,
80
71
  finalized: !1,
81
- pdfVariants: f,
72
+ pdfVariants: c,
82
73
  disabled: !1
83
74
  });
84
75
  }
85
- await h(t, "participations", r, B, i);
76
+ await w(t, "participations", n, F, s);
86
77
  }
87
78
  }
88
- async function K(t, i) {
89
- const n = await d("students", V), c = A(n, "participationId");
79
+ async function G(t, s) {
80
+ const e = await f("students", C), o = O(e, "participationId");
90
81
  await Promise.all(
91
- Object.entries(c).map(
92
- ([s, m]) => h(
82
+ Object.entries(o).map(
83
+ ([i, n]) => w(
93
84
  t,
94
- `participations/${s}/students`,
95
- m,
96
- E,
97
- i
85
+ `participations/${i}/students`,
86
+ n,
87
+ Q,
88
+ s
98
89
  )
99
90
  )
100
91
  );
101
92
  }
102
- async function L(t, i) {
103
- const n = await d("variants", y), c = await Promise.all(
104
- n.flatMap((s) => I([...s.variantIds, ...s.pdfVariantIds]).map(async (r) => {
105
- const a = p.join("variants", s.id, r, "answers.json");
106
- let e;
93
+ async function H(t, s) {
94
+ const e = await f("variants", j), o = await Promise.all(
95
+ e.flatMap((i) => y([...i.variantIds, ...i.pdfVariantIds]).map(async (a) => {
96
+ const r = m.join("variants", i.id, a, "answers.json");
97
+ let c;
107
98
  try {
108
- e = await j(a, "utf8");
99
+ c = await b(r, "utf8");
109
100
  } catch {
110
- u(`Cannot find schema for variant ${r}. Use \`quizms variants\` to generate it.`);
101
+ u(`Cannot find schema for variant ${a}. Use \`quizms variants\` to generate it.`);
111
102
  }
112
103
  try {
113
- return z(P, JSON.parse(e));
114
- } catch (f) {
115
- u(`Invalid schema for variant ${r}: ${f}`);
104
+ return P(V, JSON.parse(c));
105
+ } catch (d) {
106
+ u(`Invalid schema for variant ${a}: ${d}`);
116
107
  }
117
108
  }))
118
109
  );
119
- await h(t, "variants", c, J, i);
110
+ await w(t, "variants", o, B, s);
120
111
  }
121
- async function T(t, i) {
122
- const n = await d("websites", F);
123
- await h(t, "websites", n, R, i);
112
+ async function K(t, s) {
113
+ const e = await f("websites", k);
114
+ await w(t, "websites", e, E, s);
124
115
  }
125
- async function X(t, i) {
126
- const n = await d("contests", g), c = await d("variants", y), s = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-"), m = n.map(async (r) => {
127
- const a = c.find((f) => f.id === r.id);
128
- a || u(`Missing variants configuration for contest ${r.id}.`);
129
- const e = I([...a.variantIds, ...a.pdfVariantIds]).map(
130
- async (f) => {
131
- const o = p.join("variants", a.id, f), v = p.join("statements", a.id, f);
132
- return (await k("*", { cwd: o })).flatMap((l) => {
133
- const $ = p.extname(l);
116
+ async function L(t, s) {
117
+ const e = await v(), o = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-"), i = e.map(async (n) => {
118
+ const a = y([...n.variantIds, ...n.pdfVariantIds]).map(
119
+ async (r) => {
120
+ const c = m.join("variants", n.id, r), d = m.join("statements", n.id, r);
121
+ return (await A("*", { cwd: c })).flatMap((p) => {
122
+ const h = m.extname(p);
134
123
  return [
135
124
  [
136
- p.join(o, l),
137
- p.join(v, `${p.basename(l, $)}-${s}${$}`)
125
+ m.join(c, p),
126
+ m.join(d, `${m.basename(p, h)}-${o}${h}`)
138
127
  ],
139
- [p.join(o, l), p.join(v, l)]
128
+ [m.join(c, p), m.join(d, p)]
140
129
  ];
141
130
  });
142
131
  }
143
132
  );
144
- return (await Promise.all(e)).flat();
133
+ return (await Promise.all(a)).flat();
145
134
  });
146
- await Z(t, "statements", (await Promise.all(m)).flat(), i);
135
+ await R(t, "statements", (await Promise.all(i)).flat(), s);
147
136
  }
148
137
  export {
149
- vt as default
138
+ ht as default
150
139
  };
151
140
  //# sourceMappingURL=import.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"import.js","sources":["../../../src/cli/import.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport {\n contestSchema,\n type Participation,\n participationSchema,\n studentSchema,\n type VariantsConfig,\n variantSchema,\n variantsConfigSchema,\n} from \"@olinfo/quizms/models\";\nimport { Rng, validate } from \"@olinfo/quizms/utils\";\nimport { fatal, load, success, warning } from \"@olinfo/quizms/utils-node\";\nimport { deleteApp } from \"firebase-admin/app\";\nimport type { Firestore } from \"firebase-admin/firestore\";\nimport { groupBy, uniq } from \"lodash-es\";\nimport picomatch from \"picomatch\";\nimport { glob } from \"tinyglobby\";\nimport z from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nimport { importCollection } from \"./utils/collection\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n variantConverter,\n websiteConverter,\n} from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { importStorage } from \"./utils/storage\";\nimport { importUsers } from \"./utils/users\";\n\ntype ImportOptions = {\n config: string;\n skipExisting?: true;\n delete?: true;\n force?: true;\n\n admins?: true;\n contests?: true;\n schools?: true;\n statements?: true;\n students?: true;\n teachers?: true;\n variants?: true;\n websites?: true;\n};\n\nexport default async function importData(options: ImportOptions) {\n process.env.QUIZMS_MODE = \"contest\";\n\n if (!existsSync(\"data\")) {\n fatal(\"Cannot find data directory. Make sure you're inside a QuizMS project.\");\n }\n\n const collections: (keyof ImportOptions)[] = [\n \"admins\",\n \"contests\",\n \"schools\",\n \"statements\",\n \"students\",\n \"teachers\",\n \"variants\",\n \"websites\",\n ];\n if (collections.every((key) => !options[key])) {\n warning(\"`Nothing to import. Use `--help` for usage.`\");\n return;\n }\n\n const { app, bucket, db } = await initializeFirebase();\n\n if (options.admins) {\n await importAdmins(db, options);\n }\n if (options.contests) {\n await importContests(db, options);\n }\n if (options.schools || options.teachers) {\n await importParticipations(db, options);\n }\n if (options.students) {\n await importStudents(db, options);\n }\n if (options.variants) {\n await importVariants(db, options);\n }\n if (options.websites) {\n await importWebsites(db, options);\n }\n if (options.statements) {\n await importStatements(bucket, options);\n }\n\n success(\"All done!\");\n await deleteApp(app);\n}\n\nasync function importAdmins(db: Firestore, options: ImportOptions) {\n const users = await load(\"admins\", userSchema.omit({ role: true }));\n await importUsers(db, \"admin\", users, options);\n}\n\nasync function importContests(db: Firestore, options: ImportOptions) {\n const contests = await load(\"contests\", contestSchema);\n await importCollection(db, \"contests\", contests, contestConverter, options);\n}\n\nasync function importParticipations(db: Firestore, options: ImportOptions) {\n const schoolSchema = participationSchema\n .omit({\n schoolId: true,\n contestId: true,\n })\n .extend({\n contestIds: z.union([z.string(), z.array(z.string())]).default(\"*\"),\n password: z.string(),\n });\n const schools = await load(\"schools\", schoolSchema);\n const contests = await load(\"contests\", contestSchema);\n let configs: VariantsConfig[] | undefined;\n\n if (options.teachers) {\n const teachers = schools.map((school) => ({\n name: school.name,\n username: school.id,\n password: school.password,\n }));\n await importUsers(db, \"teacher\", teachers, options);\n }\n\n if (options.schools) {\n const participations: Participation[] = [];\n\n for (const contest of contests) {\n for (const school of schools) {\n if (!picomatch.isMatch(contest.id, school.contestIds)) continue;\n\n let pdfVariants: string[] | undefined;\n if (contest.hasVariants) {\n if (school.pdfVariants) {\n pdfVariants = school.pdfVariants.map((id) => `${contest.id}-${id}`);\n } else {\n if (!configs) {\n configs = await load(\"variants\", variantsConfigSchema);\n }\n const config = configs.find((c) => c.id === contest.id);\n if (!config) {\n fatal(`Missing variants configuration for contest ${contest.id}.`);\n }\n\n const rng = new Rng(`${config.secret}-${config.id}-${school.id}-participation`);\n pdfVariants = rng.sample(config.pdfVariantIds, config.pdfPerSchool);\n }\n }\n\n participations.push({\n id: `${school.id}-${contest.id}`,\n schoolId: school.id,\n contestId: contest.id,\n name: school.name,\n finalized: false,\n pdfVariants,\n disabled: false,\n });\n }\n }\n\n await importCollection(db, \"participations\", participations, participationConverter, options);\n }\n}\n\nasync function importStudents(db: Firestore, options: ImportOptions) {\n const students = await load(\"students\", studentSchema);\n const participations = groupBy(students, \"participationId\");\n await Promise.all(\n Object.entries(participations).map(([participationId, students]) =>\n importCollection(\n db,\n `participations/${participationId}/students`,\n students,\n studentConverter,\n options,\n ),\n ),\n );\n}\n\nasync function importVariants(db: Firestore, options: ImportOptions) {\n const variantsConfig = await load(\"variants\", variantsConfigSchema);\n const variants = await Promise.all(\n variantsConfig.flatMap((config) => {\n const ids = uniq([...config.variantIds, ...config.pdfVariantIds]);\n return ids.map(async (id) => {\n const fileName = path.join(\"variants\", config.id, id, \"answers.json\");\n let schema: string;\n try {\n schema = await readFile(fileName, \"utf8\");\n } catch {\n fatal(`Cannot find schema for variant ${id}. Use \\`quizms variants\\` to generate it.`);\n }\n try {\n return validate(variantSchema, JSON.parse(schema));\n } catch (err) {\n fatal(`Invalid schema for variant ${id}: ${err}`);\n }\n });\n }),\n );\n await importCollection(db, \"variants\", variants, variantConverter, options);\n}\n\nasync function importWebsites(db: Firestore, options: ImportOptions) {\n const websites = await load(\"websites\", websiteSchema);\n await importCollection(db, \"websites\", websites, websiteConverter, options);\n}\n\nasync function importStatements(bucket: Bucket, options: ImportOptions) {\n const contests = await load(\"contests\", contestSchema);\n const variantsConfig = await load(\"variants\", variantsConfigSchema);\n\n const timestamp = new Date().toISOString().replace(/:/g, \"-\");\n\n const statements = contests.map(async (contest) => {\n const config = variantsConfig.find((c) => c.id === contest.id);\n if (!config) {\n fatal(`Missing variants configuration for contest ${contest.id}.`);\n }\n\n const files = uniq([...config.variantIds, ...config.pdfVariantIds]).map(\n async (id): Promise<[string, string][]> => {\n const localDir = path.join(\"variants\", config.id, id);\n const remoteDir = path.join(\"statements\", config.id, id);\n const files = await glob(\"*\", { cwd: localDir });\n\n return files.flatMap((file) => {\n const ext = path.extname(file);\n return [\n [\n path.join(localDir, file),\n path.join(remoteDir, `${path.basename(file, ext)}-${timestamp}${ext}`),\n ],\n [path.join(localDir, file), path.join(remoteDir, file)],\n ];\n });\n },\n );\n return (await Promise.all(files)).flat();\n });\n await importStorage(bucket, \"statements\", (await Promise.all(statements)).flat(), options);\n}\n"],"names":["importData","options","existsSync","fatal","key","warning","app","bucket","db","initializeFirebase","importAdmins","importContests","importParticipations","importStudents","importVariants","importWebsites","importStatements","success","deleteApp","users","load","userSchema","importUsers","contests","contestSchema","importCollection","contestConverter","schoolSchema","participationSchema","z","schools","configs","teachers","school","participations","contest","picomatch","pdfVariants","id","variantsConfigSchema","config","c","Rng","participationConverter","students","studentSchema","groupBy","participationId","studentConverter","variantsConfig","variants","uniq","fileName","path","schema","readFile","validate","variantSchema","err","variantConverter","websites","websiteSchema","websiteConverter","timestamp","statements","files","localDir","remoteDir","glob","file","ext","importStorage"],"mappings":"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"}
1
+ {"version":3,"file":"import.js","sources":["../../../src/cli/import.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport {\n contestSchema,\n type Participation,\n participationSchema,\n studentSchema,\n variantSchema,\n variantsConfigSchema,\n} from \"@olinfo/quizms/models\";\nimport { Rng, validate } from \"@olinfo/quizms/utils\";\nimport { fatal, load, loadContests, success, warning } from \"@olinfo/quizms/utils-node\";\nimport { deleteApp } from \"firebase-admin/app\";\nimport type { Firestore } from \"firebase-admin/firestore\";\nimport { groupBy, uniq } from \"lodash-es\";\nimport picomatch from \"picomatch\";\nimport { glob } from \"tinyglobby\";\nimport z from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nimport { importCollection } from \"./utils/collection\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n variantConverter,\n websiteConverter,\n} from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { importStorage } from \"./utils/storage\";\nimport { importUsers } from \"./utils/users\";\n\ntype ImportOptions = {\n config: string;\n skipExisting?: true;\n delete?: true;\n force?: true;\n\n admins?: true;\n contests?: true;\n schools?: true;\n statements?: true;\n students?: true;\n teachers?: true;\n variants?: true;\n websites?: true;\n};\n\nexport default async function importData(options: ImportOptions) {\n process.env.QUIZMS_MODE = \"contest\";\n\n if (!existsSync(\"data\")) {\n fatal(\"Cannot find data directory. Make sure you're inside a QuizMS project.\");\n }\n\n const collections: (keyof ImportOptions)[] = [\n \"admins\",\n \"contests\",\n \"schools\",\n \"statements\",\n \"students\",\n \"teachers\",\n \"variants\",\n \"websites\",\n ];\n if (collections.every((key) => !options[key])) {\n warning(\"`Nothing to import. Use `--help` for usage.`\");\n return;\n }\n\n const { app, bucket, db } = await initializeFirebase();\n\n if (options.admins) {\n await importAdmins(db, options);\n }\n if (options.contests) {\n await importContests(db, options);\n }\n if (options.schools || options.teachers) {\n await importParticipations(db, options);\n }\n if (options.students) {\n await importStudents(db, options);\n }\n if (options.variants) {\n await importVariants(db, options);\n }\n if (options.websites) {\n await importWebsites(db, options);\n }\n if (options.statements) {\n await importStatements(bucket, options);\n }\n\n success(\"All done!\");\n await deleteApp(app);\n}\n\nasync function importAdmins(db: Firestore, options: ImportOptions) {\n const users = await load(\"admins\", userSchema.omit({ role: true }));\n await importUsers(db, \"admin\", users, options);\n}\n\nasync function importContests(db: Firestore, options: ImportOptions) {\n const contests = await load(\"contests\", contestSchema);\n await importCollection(db, \"contests\", contests, contestConverter, options);\n}\n\nasync function importParticipations(db: Firestore, options: ImportOptions) {\n const schoolSchema = participationSchema\n .omit({\n schoolId: true,\n contestId: true,\n })\n .extend({\n contestIds: z.union([z.string(), z.array(z.string())]).default(\"*\"),\n password: z.string(),\n });\n const schools = await load(\"schools\", schoolSchema);\n const contests = await loadContests();\n\n if (options.teachers) {\n const teachers = schools.map((school) => ({\n name: school.name,\n username: school.id,\n password: school.password,\n }));\n await importUsers(db, \"teacher\", teachers, options);\n }\n\n if (options.schools) {\n const participations: Participation[] = [];\n\n for (const contest of contests) {\n for (const school of schools) {\n if (!picomatch.isMatch(contest.id, school.contestIds)) continue;\n\n let pdfVariants: string[] | undefined;\n if (contest.hasVariants) {\n if (school.pdfVariants) {\n pdfVariants = school.pdfVariants.map((id) => `${contest.id}-${id}`);\n } else {\n const rng = new Rng(`${contest.secret}-${contest.id}-${school.id}-participation`);\n pdfVariants = rng.sample(contest.pdfVariantIds, contest.pdfPerSchool);\n }\n }\n\n participations.push({\n id: `${school.id}-${contest.id}`,\n schoolId: school.id,\n contestId: contest.id,\n name: school.name,\n finalized: false,\n pdfVariants,\n disabled: false,\n });\n }\n }\n\n await importCollection(db, \"participations\", participations, participationConverter, options);\n }\n}\n\nasync function importStudents(db: Firestore, options: ImportOptions) {\n const students = await load(\"students\", studentSchema);\n const participations = groupBy(students, \"participationId\");\n await Promise.all(\n Object.entries(participations).map(([participationId, students]) =>\n importCollection(\n db,\n `participations/${participationId}/students`,\n students,\n studentConverter,\n options,\n ),\n ),\n );\n}\n\nasync function importVariants(db: Firestore, options: ImportOptions) {\n const variantsConfig = await load(\"variants\", variantsConfigSchema);\n const variants = await Promise.all(\n variantsConfig.flatMap((config) => {\n const ids = uniq([...config.variantIds, ...config.pdfVariantIds]);\n return ids.map(async (id) => {\n const fileName = path.join(\"variants\", config.id, id, \"answers.json\");\n let schema: string;\n try {\n schema = await readFile(fileName, \"utf8\");\n } catch {\n fatal(`Cannot find schema for variant ${id}. Use \\`quizms variants\\` to generate it.`);\n }\n try {\n return validate(variantSchema, JSON.parse(schema));\n } catch (err) {\n fatal(`Invalid schema for variant ${id}: ${err}`);\n }\n });\n }),\n );\n await importCollection(db, \"variants\", variants, variantConverter, options);\n}\n\nasync function importWebsites(db: Firestore, options: ImportOptions) {\n const websites = await load(\"websites\", websiteSchema);\n await importCollection(db, \"websites\", websites, websiteConverter, options);\n}\n\nasync function importStatements(bucket: Bucket, options: ImportOptions) {\n const contests = await loadContests();\n\n const timestamp = new Date().toISOString().replace(/:/g, \"-\");\n\n const statements = contests.map(async (config) => {\n const files = uniq([...config.variantIds, ...config.pdfVariantIds]).map(\n async (id): Promise<[string, string][]> => {\n const localDir = path.join(\"variants\", config.id, id);\n const remoteDir = path.join(\"statements\", config.id, id);\n const files = await glob(\"*\", { cwd: localDir });\n\n return files.flatMap((file) => {\n const ext = path.extname(file);\n return [\n [\n path.join(localDir, file),\n path.join(remoteDir, `${path.basename(file, ext)}-${timestamp}${ext}`),\n ],\n [path.join(localDir, file), path.join(remoteDir, file)],\n ];\n });\n },\n );\n return (await Promise.all(files)).flat();\n });\n await importStorage(bucket, \"statements\", (await Promise.all(statements)).flat(), options);\n}\n"],"names":["importData","options","existsSync","fatal","key","warning","app","bucket","db","initializeFirebase","importAdmins","importContests","importParticipations","importStudents","importVariants","importWebsites","importStatements","success","deleteApp","users","load","userSchema","importUsers","contests","contestSchema","importCollection","contestConverter","schoolSchema","participationSchema","z","schools","loadContests","teachers","school","participations","contest","picomatch","pdfVariants","id","Rng","participationConverter","students","studentSchema","groupBy","participationId","studentConverter","variantsConfig","variantsConfigSchema","variants","config","uniq","fileName","path","schema","readFile","validate","variantSchema","err","variantConverter","websites","websiteSchema","websiteConverter","timestamp","statements","files","localDir","remoteDir","glob","file","ext","importStorage"],"mappings":"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"}
@@ -1,37 +1,36 @@
1
- import i from "node:crypto";
2
- import { Rng as c } from "@olinfo/quizms/utils";
3
- import { userConverter as p, participationConverter as o, contestConverter as h, studentConverter as a, variantConverter as d } from "../../cli/utils/converters-admin.js";
1
+ import { Rng as i } from "@olinfo/quizms/utils";
2
+ import { userConverter as c, participationConverter as o, contestConverter as p, studentConverter as a, variantConverter as h } from "../../cli/utils/converters-admin.js";
4
3
  import { db as e } from "../common.js";
5
- const u = new c(i.randomUUID());
6
- async function l(t) {
7
- return (await e.doc(`contests/${t}`).withConverter(h).get()).data();
4
+ const u = new i();
5
+ async function v(t) {
6
+ return (await e.doc(`contests/${t}`).withConverter(p).get()).data();
8
7
  }
9
- async function m(t) {
8
+ async function C(t) {
10
9
  return (await e.doc(`participations/${t}`).withConverter(o).get()).data();
11
10
  }
12
- async function f(t, n) {
13
- return (await e.collection(`participations/${t}/students`).where("token", "==", n).withConverter(a).get()).docs.map((s) => s.data());
11
+ async function l(t, n) {
12
+ return (await e.collection(`participations/${t}/students`).where("token", "==", n).withConverter(a).get()).docs.map((r) => r.data());
14
13
  }
15
- async function y(t) {
14
+ async function f(t) {
16
15
  return (await e.collection("participations").withConverter(o).where("token", "==", t).get()).docs[0]?.data();
17
16
  }
18
- async function $(t) {
19
- return (await e.collection("users").where("username", "==", t).withConverter(p).get()).docs[0]?.data();
17
+ async function m(t) {
18
+ return (await e.collection("users").where("username", "==", t).withConverter(c).get()).docs[0]?.data();
20
19
  }
21
- async function k(t) {
22
- const n = await e.collection("variants").withConverter(d).where("contestId", "==", t).where("isOnline", "==", !0).get();
20
+ async function y(t) {
21
+ const n = await e.collection("variants").withConverter(h).where("contestId", "==", t).where("isOnline", "==", !0).get();
23
22
  return u.choice(n.docs).data();
24
23
  }
25
- async function P(t, n) {
24
+ async function $(t, n) {
26
25
  return (await e.collection(`participations/${t}/students`).withConverter(a).where("userDataHash", "==", n).where("disabled", "!=", !0).get()).docs[0]?.data();
27
26
  }
28
27
  export {
29
- l as getContest,
30
- m as getParticipation,
31
- y as getParticipationByToken,
32
- f as getParticipationStudents,
33
- k as getRandomVariant,
34
- P as getStudentByHash,
35
- $ as getUser
28
+ v as getContest,
29
+ C as getParticipation,
30
+ f as getParticipationByToken,
31
+ l as getParticipationStudents,
32
+ y as getRandomVariant,
33
+ $ as getStudentByHash,
34
+ m as getUser
36
35
  };
37
36
  //# sourceMappingURL=queries.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queries.js","sources":["../../../../src/functions/api/queries.ts"],"sourcesContent":["import crypto from \"node:crypto\";\n\nimport { Rng } from \"@olinfo/quizms/utils\";\n\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n userConverter,\n variantConverter,\n} from \"~/cli/utils/converters-admin\";\nimport { db } from \"~/functions/common\";\n\nconst rng = new Rng(crypto.randomUUID());\n\nexport async function getContest(contestId: string) {\n const snapshot = await db.doc(`contests/${contestId}`).withConverter(contestConverter).get();\n return snapshot.data()!;\n}\n\nexport async function getParticipation(participationId: string) {\n const snapshot = await db\n .doc(`participations/${participationId}`)\n .withConverter(participationConverter)\n .get();\n return snapshot.data();\n}\n\nexport async function getParticipationStudents(participationId: string, token: string) {\n const snapshot = await db\n .collection(`participations/${participationId}/students`)\n .where(\"token\", \"==\", token)\n .withConverter(studentConverter)\n .get();\n return snapshot.docs.map((doc) => doc.data());\n}\n\nexport async function getParticipationByToken(token: string) {\n const snapshot = await db\n .collection(\"participations\")\n .withConverter(participationConverter)\n .where(\"token\", \"==\", token)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getUser(username: string) {\n const snapshot = await db\n .collection(\"users\")\n .where(\"username\", \"==\", username)\n .withConverter(userConverter)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getRandomVariant(contestId: string) {\n const snapshot = await db\n .collection(\"variants\")\n .withConverter(variantConverter)\n .where(\"contestId\", \"==\", contestId)\n .where(\"isOnline\", \"==\", true)\n .get();\n return rng.choice(snapshot.docs).data();\n}\nexport async function getStudentByHash(participationId: string, userDataHash: string) {\n const snapshot = await db\n .collection(`participations/${participationId}/students`)\n .withConverter(studentConverter)\n .where(\"userDataHash\", \"==\", userDataHash)\n .where(\"disabled\", \"!=\", true)\n .get();\n return snapshot.docs[0]?.data();\n}\n"],"names":["rng","Rng","crypto","getContest","contestId","db","contestConverter","getParticipation","participationId","participationConverter","getParticipationStudents","token","studentConverter","doc","getParticipationByToken","getUser","username","userConverter","getRandomVariant","snapshot","variantConverter","getStudentByHash","userDataHash"],"mappings":";;;;AAaA,MAAMA,IAAM,IAAIC,EAAIC,EAAO,YAAY;AAEvC,eAAsBC,EAAWC,GAAmB;AAElD,UADiB,MAAMC,EAAG,IAAI,YAAYD,CAAS,EAAE,EAAE,cAAcE,CAAgB,EAAE,IAAA,GACvE,KAAA;AAClB;AAEA,eAAsBC,EAAiBC,GAAyB;AAK9D,UAJiB,MAAMH,EACpB,IAAI,kBAAkBG,CAAe,EAAE,EACvC,cAAcC,CAAsB,EACpC,IAAA,GACa,KAAA;AAClB;AAEA,eAAsBC,EAAyBF,GAAyBG,GAAe;AAMrF,UALiB,MAAMN,EACpB,WAAW,kBAAkBG,CAAe,WAAW,EACvD,MAAM,SAAS,MAAMG,CAAK,EAC1B,cAAcC,CAAgB,EAC9B,IAAA,GACa,KAAK,IAAI,CAACC,MAAQA,EAAI,MAAM;AAC9C;AAEA,eAAsBC,EAAwBH,GAAe;AAM3D,UALiB,MAAMN,EACpB,WAAW,gBAAgB,EAC3B,cAAcI,CAAsB,EACpC,MAAM,SAAS,MAAME,CAAK,EAC1B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBI,EAAQC,GAAkB;AAM9C,UALiB,MAAMX,EACpB,WAAW,OAAO,EAClB,MAAM,YAAY,MAAMW,CAAQ,EAChC,cAAcC,CAAa,EAC3B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBC,EAAiBd,GAAmB;AACxD,QAAMe,IAAW,MAAMd,EACpB,WAAW,UAAU,EACrB,cAAce,CAAgB,EAC9B,MAAM,aAAa,MAAMhB,CAAS,EAClC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA;AACH,SAAOJ,EAAI,OAAOmB,EAAS,IAAI,EAAE,KAAA;AACnC;AACA,eAAsBE,EAAiBb,GAAyBc,GAAsB;AAOpF,UANiB,MAAMjB,EACpB,WAAW,kBAAkBG,CAAe,WAAW,EACvD,cAAcI,CAAgB,EAC9B,MAAM,gBAAgB,MAAMU,CAAY,EACxC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;"}
1
+ {"version":3,"file":"queries.js","sources":["../../../../src/functions/api/queries.ts"],"sourcesContent":["import { Rng } from \"@olinfo/quizms/utils\";\n\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n userConverter,\n variantConverter,\n} from \"~/cli/utils/converters-admin\";\nimport { db } from \"~/functions/common\";\n\nconst rng = new Rng();\n\nexport async function getContest(contestId: string) {\n const snapshot = await db.doc(`contests/${contestId}`).withConverter(contestConverter).get();\n return snapshot.data()!;\n}\n\nexport async function getParticipation(participationId: string) {\n const snapshot = await db\n .doc(`participations/${participationId}`)\n .withConverter(participationConverter)\n .get();\n return snapshot.data();\n}\n\nexport async function getParticipationStudents(participationId: string, token: string) {\n const snapshot = await db\n .collection(`participations/${participationId}/students`)\n .where(\"token\", \"==\", token)\n .withConverter(studentConverter)\n .get();\n return snapshot.docs.map((doc) => doc.data());\n}\n\nexport async function getParticipationByToken(token: string) {\n const snapshot = await db\n .collection(\"participations\")\n .withConverter(participationConverter)\n .where(\"token\", \"==\", token)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getUser(username: string) {\n const snapshot = await db\n .collection(\"users\")\n .where(\"username\", \"==\", username)\n .withConverter(userConverter)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getRandomVariant(contestId: string) {\n const snapshot = await db\n .collection(\"variants\")\n .withConverter(variantConverter)\n .where(\"contestId\", \"==\", contestId)\n .where(\"isOnline\", \"==\", true)\n .get();\n return rng.choice(snapshot.docs).data();\n}\nexport async function getStudentByHash(participationId: string, userDataHash: string) {\n const snapshot = await db\n .collection(`participations/${participationId}/students`)\n .withConverter(studentConverter)\n .where(\"userDataHash\", \"==\", userDataHash)\n .where(\"disabled\", \"!=\", true)\n .get();\n return snapshot.docs[0]?.data();\n}\n"],"names":["rng","Rng","getContest","contestId","db","contestConverter","getParticipation","participationId","participationConverter","getParticipationStudents","token","studentConverter","doc","getParticipationByToken","getUser","username","userConverter","getRandomVariant","snapshot","variantConverter","getStudentByHash","userDataHash"],"mappings":";;;AAWA,MAAMA,IAAM,IAAIC,EAAA;AAEhB,eAAsBC,EAAWC,GAAmB;AAElD,UADiB,MAAMC,EAAG,IAAI,YAAYD,CAAS,EAAE,EAAE,cAAcE,CAAgB,EAAE,IAAA,GACvE,KAAA;AAClB;AAEA,eAAsBC,EAAiBC,GAAyB;AAK9D,UAJiB,MAAMH,EACpB,IAAI,kBAAkBG,CAAe,EAAE,EACvC,cAAcC,CAAsB,EACpC,IAAA,GACa,KAAA;AAClB;AAEA,eAAsBC,EAAyBF,GAAyBG,GAAe;AAMrF,UALiB,MAAMN,EACpB,WAAW,kBAAkBG,CAAe,WAAW,EACvD,MAAM,SAAS,MAAMG,CAAK,EAC1B,cAAcC,CAAgB,EAC9B,IAAA,GACa,KAAK,IAAI,CAACC,MAAQA,EAAI,MAAM;AAC9C;AAEA,eAAsBC,EAAwBH,GAAe;AAM3D,UALiB,MAAMN,EACpB,WAAW,gBAAgB,EAC3B,cAAcI,CAAsB,EACpC,MAAM,SAAS,MAAME,CAAK,EAC1B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBI,EAAQC,GAAkB;AAM9C,UALiB,MAAMX,EACpB,WAAW,OAAO,EAClB,MAAM,YAAY,MAAMW,CAAQ,EAChC,cAAcC,CAAa,EAC3B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBC,EAAiBd,GAAmB;AACxD,QAAMe,IAAW,MAAMd,EACpB,WAAW,UAAU,EACrB,cAAce,CAAgB,EAC9B,MAAM,aAAa,MAAMhB,CAAS,EAClC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA;AACH,SAAOH,EAAI,OAAOkB,EAAS,IAAI,EAAE,KAAA;AACnC;AACA,eAAsBE,EAAiBb,GAAyBc,GAAsB;AAOpF,UANiB,MAAMjB,EACpB,WAAW,kBAAkBG,CAAe,WAAW,EACvD,cAAcI,CAAgB,EAC9B,MAAM,gBAAgB,MAAMU,CAAY,EACxC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;"}
@@ -1,38 +1,38 @@
1
- import D, { createHash as I } from "node:crypto";
2
- import { studentSchema as u, getNormalizedUserData as w } from "@olinfo/quizms/models";
3
- import { TRPCError as r } from "@trpc/server";
4
- import { Timestamp as i } from "firebase-admin/firestore";
5
- import * as l from "firebase-functions/logger";
1
+ import D, { hash as I } from "node:crypto";
2
+ import { studentSchema as p, getNormalizedUserData as w } from "@olinfo/quizms/models";
3
+ import { TRPCError as i } from "@trpc/server";
4
+ import { Timestamp as r } from "firebase-admin/firestore";
5
+ import * as h from "firebase-functions/logger";
6
6
  import s from "zod";
7
- import { db as p, auth as m } from "../common.js";
8
- import { getParticipationByToken as h, getContest as k, getStudentByHash as v, getRandomVariant as T } from "./queries.js";
7
+ import { db as u, auth as m } from "../common.js";
8
+ import { getParticipationByToken as l, getContest as k, getStudentByHash as v, getRandomVariant as T } from "./queries.js";
9
9
  import { publicProcedure as C } from "./trpc.js";
10
10
  function R(o, t) {
11
- return I("sha256").update(w(o, { userData: t })).digest().toString("base64url");
11
+ return I("sha256", w(o, { userData: t }));
12
12
  }
13
13
  const U = C.input(
14
14
  s.strictObject({
15
15
  contestId: s.string(),
16
16
  token: s.string(),
17
- userData: u.shape.userData.unwrap().required(),
18
- extraData: u.shape.extraData.unwrap()
17
+ userData: p.shape.userData.unwrap().required(),
18
+ extraData: p.shape.extraData.unwrap()
19
19
  })
20
20
  ).mutation(async (o) => {
21
21
  const { input: t } = o;
22
- l.info("Student login request received", { data: t });
23
- const a = await h(t.token);
22
+ h.info("Student login request received", { data: t });
23
+ const a = await l(t.token);
24
24
  if (!a)
25
- throw new r({ code: "BAD_REQUEST", message: "Codice prova non valido" });
25
+ throw new i({ code: "BAD_REQUEST", message: "Codice prova non valido" });
26
26
  if (a.contestId !== t.contestId)
27
- throw new r({ code: "FORBIDDEN", message: "Codice prova non valido" });
28
- const g = await k(a.contestId), n = D.randomUUID(), d = R(g, t.userData), e = await v(a.id, d);
27
+ throw new i({ code: "FORBIDDEN", message: "Codice prova non valido" });
28
+ const f = await k(a.contestId), n = D.randomUUID(), d = R(f, t.userData), e = await v(a.id, d);
29
29
  if (e) {
30
30
  if (e.token !== t.token)
31
- throw new r({
31
+ throw new i({
32
32
  code: "FORBIDDEN",
33
33
  message: "Hai già partecipato a questa gara"
34
34
  });
35
- return await p.doc(`studentRestores/${n}`).set({
35
+ return await u.doc(`studentRestores/${n}`).set({
36
36
  studentId: e.id,
37
37
  participationId: e.participationId,
38
38
  token: t.token,
@@ -40,7 +40,7 @@ const U = C.input(
40
40
  surname: t.userData.surname,
41
41
  approvalCode: Math.round(Math.random() * 1e3),
42
42
  status: "pending",
43
- createdAt: i.now()
43
+ createdAt: r.now()
44
44
  }), m.createCustomToken(n, {
45
45
  role: "student",
46
46
  studentId: e.id,
@@ -48,7 +48,7 @@ const U = C.input(
48
48
  variant: e.variant
49
49
  });
50
50
  }
51
- const c = await T(t.contestId), f = await p.collection(`participations/${a.id}/students`).add({
51
+ const c = await T(t.contestId), g = await u.collection(`participations/${a.id}/students`).add({
52
52
  uid: n,
53
53
  userData: t.userData,
54
54
  userDataHash: d,
@@ -62,12 +62,12 @@ const U = C.input(
62
62
  variant: c.id,
63
63
  answers: {},
64
64
  extraData: t.extraData,
65
- createdAt: i.now(),
66
- updatedAt: i.now()
65
+ createdAt: r.now(),
66
+ updatedAt: r.now()
67
67
  });
68
68
  return m.createCustomToken(n, {
69
69
  role: "student",
70
- studentId: f.id,
70
+ studentId: g.id,
71
71
  participationId: a.id,
72
72
  variant: c
73
73
  });
@@ -1 +1 @@
1
- {"version":3,"file":"student.js","sources":["../../../../src/functions/api/student.ts"],"sourcesContent":["import crypto, { createHash } from \"node:crypto\";\n\nimport {\n type Contest,\n getNormalizedUserData,\n type Student,\n studentSchema,\n} from \"@olinfo/quizms/models\";\nimport { TRPCError } from \"@trpc/server\";\nimport { Timestamp } from \"firebase-admin/firestore\";\nimport * as logger from \"firebase-functions/logger\";\nimport z from \"zod\";\n\nimport { auth, db } from \"../common\";\nimport { getContest, getParticipationByToken, getRandomVariant, getStudentByHash } from \"./queries\";\nimport { publicProcedure } from \"./trpc\";\n\nfunction studentHash(contest: Contest, userData: NonNullable<Student[\"userData\"]>) {\n return createHash(\"sha256\")\n .update(getNormalizedUserData(contest, { userData }))\n .digest()\n .toString(\"base64url\");\n}\n\nexport const studentLogin = publicProcedure\n .input(\n z.strictObject({\n contestId: z.string(),\n token: z.string(),\n userData: studentSchema.shape.userData.unwrap().required(),\n extraData: studentSchema.shape.extraData.unwrap(),\n }),\n )\n .mutation(async (opts) => {\n const { input: data } = opts;\n logger.info(\"Student login request received\", { data });\n\n const participation = await getParticipationByToken(data.token);\n if (!participation) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Codice prova non valido\" });\n }\n if (participation.contestId !== data.contestId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Codice prova non valido\" });\n }\n\n const contest = await getContest(participation.contestId);\n\n const uid = crypto.randomUUID();\n const userDataHash = studentHash(contest, data.userData);\n\n const duplicated = await getStudentByHash(participation.id, userDataHash);\n if (duplicated) {\n if (duplicated.token !== data.token) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Hai già partecipato a questa gara\",\n });\n }\n\n await db.doc(`studentRestores/${uid}`).set({\n studentId: duplicated.id,\n participationId: duplicated.participationId,\n token: data.token,\n name: data.userData.name,\n surname: data.userData.surname,\n approvalCode: Math.round(Math.random() * 1000),\n status: \"pending\",\n createdAt: Timestamp.now(),\n });\n\n return auth.createCustomToken(uid, {\n role: \"student\",\n studentId: duplicated.id,\n participationId: duplicated.participationId,\n variant: duplicated.variant,\n });\n }\n\n const variant = await getRandomVariant(data.contestId);\n\n const studentRef = await db.collection(`participations/${participation.id}/students`).add({\n uid,\n userData: data.userData,\n userDataHash,\n absent: false,\n disabled: false,\n participationId: participation.id,\n contestId: data.contestId,\n token: data.token,\n startedAt: participation.startingTime,\n finishedAt: participation.endingTime,\n variant: variant.id,\n answers: {},\n extraData: data.extraData,\n createdAt: Timestamp.now(),\n updatedAt: Timestamp.now(),\n });\n\n return auth.createCustomToken(uid, {\n role: \"student\",\n studentId: studentRef.id,\n participationId: participation.id,\n variant,\n });\n });\n"],"names":["studentHash","contest","userData","createHash","getNormalizedUserData","studentLogin","publicProcedure","z","studentSchema","opts","data","logger","participation","getParticipationByToken","TRPCError","getContest","uid","crypto","userDataHash","duplicated","getStudentByHash","db","Timestamp","auth","variant","getRandomVariant","studentRef"],"mappings":";;;;;;;;;AAiBA,SAASA,EAAYC,GAAkBC,GAA4C;AACjF,SAAOC,EAAW,QAAQ,EACvB,OAAOC,EAAsBH,GAAS,EAAE,UAAAC,EAAA,CAAU,CAAC,EACnD,SACA,SAAS,WAAW;AACzB;AAEO,MAAMG,IAAeC,EACzB;AAAA,EACCC,EAAE,aAAa;AAAA,IACb,WAAWA,EAAE,OAAA;AAAA,IACb,OAAOA,EAAE,OAAA;AAAA,IACT,UAAUC,EAAc,MAAM,SAAS,OAAA,EAAS,SAAA;AAAA,IAChD,WAAWA,EAAc,MAAM,UAAU,OAAA;AAAA,EAAO,CACjD;AACH,EACC,SAAS,OAAOC,MAAS;AACxB,QAAM,EAAE,OAAOC,EAAA,IAASD;AACxB,EAAAE,EAAO,KAAK,kCAAkC,EAAE,MAAAD,EAAA,CAAM;AAEtD,QAAME,IAAgB,MAAMC,EAAwBH,EAAK,KAAK;AAC9D,MAAI,CAACE;AACH,UAAM,IAAIE,EAAU,EAAE,MAAM,eAAe,SAAS,2BAA2B;AAEjF,MAAIF,EAAc,cAAcF,EAAK;AACnC,UAAM,IAAII,EAAU,EAAE,MAAM,aAAa,SAAS,2BAA2B;AAG/E,QAAMb,IAAU,MAAMc,EAAWH,EAAc,SAAS,GAElDI,IAAMC,EAAO,WAAA,GACbC,IAAelB,EAAYC,GAASS,EAAK,QAAQ,GAEjDS,IAAa,MAAMC,EAAiBR,EAAc,IAAIM,CAAY;AACxE,MAAIC,GAAY;AACd,QAAIA,EAAW,UAAUT,EAAK;AAC5B,YAAM,IAAII,EAAU;AAAA,QAClB,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AAGH,iBAAMO,EAAG,IAAI,mBAAmBL,CAAG,EAAE,EAAE,IAAI;AAAA,MACzC,WAAWG,EAAW;AAAA,MACtB,iBAAiBA,EAAW;AAAA,MAC5B,OAAOT,EAAK;AAAA,MACZ,MAAMA,EAAK,SAAS;AAAA,MACpB,SAASA,EAAK,SAAS;AAAA,MACvB,cAAc,KAAK,MAAM,KAAK,OAAA,IAAW,GAAI;AAAA,MAC7C,QAAQ;AAAA,MACR,WAAWY,EAAU,IAAA;AAAA,IAAI,CAC1B,GAEMC,EAAK,kBAAkBP,GAAK;AAAA,MACjC,MAAM;AAAA,MACN,WAAWG,EAAW;AAAA,MACtB,iBAAiBA,EAAW;AAAA,MAC5B,SAASA,EAAW;AAAA,IAAA,CACrB;AAAA,EACH;AAEA,QAAMK,IAAU,MAAMC,EAAiBf,EAAK,SAAS,GAE/CgB,IAAa,MAAML,EAAG,WAAW,kBAAkBT,EAAc,EAAE,WAAW,EAAE,IAAI;AAAA,IACxF,KAAAI;AAAA,IACA,UAAUN,EAAK;AAAA,IACf,cAAAQ;AAAA,IACA,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiBN,EAAc;AAAA,IAC/B,WAAWF,EAAK;AAAA,IAChB,OAAOA,EAAK;AAAA,IACZ,WAAWE,EAAc;AAAA,IACzB,YAAYA,EAAc;AAAA,IAC1B,SAASY,EAAQ;AAAA,IACjB,SAAS,CAAA;AAAA,IACT,WAAWd,EAAK;AAAA,IAChB,WAAWY,EAAU,IAAA;AAAA,IACrB,WAAWA,EAAU,IAAA;AAAA,EAAI,CAC1B;AAED,SAAOC,EAAK,kBAAkBP,GAAK;AAAA,IACjC,MAAM;AAAA,IACN,WAAWU,EAAW;AAAA,IACtB,iBAAiBd,EAAc;AAAA,IAC/B,SAAAY;AAAA,EAAA,CACD;AACH,CAAC;"}
1
+ {"version":3,"file":"student.js","sources":["../../../../src/functions/api/student.ts"],"sourcesContent":["import crypto, { hash } from \"node:crypto\";\n\nimport {\n type Contest,\n getNormalizedUserData,\n type Student,\n studentSchema,\n} from \"@olinfo/quizms/models\";\nimport { TRPCError } from \"@trpc/server\";\nimport { Timestamp } from \"firebase-admin/firestore\";\nimport * as logger from \"firebase-functions/logger\";\nimport z from \"zod\";\n\nimport { auth, db } from \"../common\";\nimport { getContest, getParticipationByToken, getRandomVariant, getStudentByHash } from \"./queries\";\nimport { publicProcedure } from \"./trpc\";\n\nfunction studentHash(contest: Contest, userData: NonNullable<Student[\"userData\"]>) {\n return hash(\"sha256\", getNormalizedUserData(contest, { userData }));\n}\n\nexport const studentLogin = publicProcedure\n .input(\n z.strictObject({\n contestId: z.string(),\n token: z.string(),\n userData: studentSchema.shape.userData.unwrap().required(),\n extraData: studentSchema.shape.extraData.unwrap(),\n }),\n )\n .mutation(async (opts) => {\n const { input: data } = opts;\n logger.info(\"Student login request received\", { data });\n\n const participation = await getParticipationByToken(data.token);\n if (!participation) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Codice prova non valido\" });\n }\n if (participation.contestId !== data.contestId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Codice prova non valido\" });\n }\n\n const contest = await getContest(participation.contestId);\n\n const uid = crypto.randomUUID();\n const userDataHash = studentHash(contest, data.userData);\n\n const duplicated = await getStudentByHash(participation.id, userDataHash);\n if (duplicated) {\n if (duplicated.token !== data.token) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Hai già partecipato a questa gara\",\n });\n }\n\n await db.doc(`studentRestores/${uid}`).set({\n studentId: duplicated.id,\n participationId: duplicated.participationId,\n token: data.token,\n name: data.userData.name,\n surname: data.userData.surname,\n approvalCode: Math.round(Math.random() * 1000),\n status: \"pending\",\n createdAt: Timestamp.now(),\n });\n\n return auth.createCustomToken(uid, {\n role: \"student\",\n studentId: duplicated.id,\n participationId: duplicated.participationId,\n variant: duplicated.variant,\n });\n }\n\n const variant = await getRandomVariant(data.contestId);\n\n const studentRef = await db.collection(`participations/${participation.id}/students`).add({\n uid,\n userData: data.userData,\n userDataHash,\n absent: false,\n disabled: false,\n participationId: participation.id,\n contestId: data.contestId,\n token: data.token,\n startedAt: participation.startingTime,\n finishedAt: participation.endingTime,\n variant: variant.id,\n answers: {},\n extraData: data.extraData,\n createdAt: Timestamp.now(),\n updatedAt: Timestamp.now(),\n });\n\n return auth.createCustomToken(uid, {\n role: \"student\",\n studentId: studentRef.id,\n participationId: participation.id,\n variant,\n });\n });\n"],"names":["studentHash","contest","userData","hash","getNormalizedUserData","studentLogin","publicProcedure","z","studentSchema","opts","data","logger","participation","getParticipationByToken","TRPCError","getContest","uid","crypto","userDataHash","duplicated","getStudentByHash","db","Timestamp","auth","variant","getRandomVariant","studentRef"],"mappings":";;;;;;;;;AAiBA,SAASA,EAAYC,GAAkBC,GAA4C;AACjF,SAAOC,EAAK,UAAUC,EAAsBH,GAAS,EAAE,UAAAC,EAAA,CAAU,CAAC;AACpE;AAEO,MAAMG,IAAeC,EACzB;AAAA,EACCC,EAAE,aAAa;AAAA,IACb,WAAWA,EAAE,OAAA;AAAA,IACb,OAAOA,EAAE,OAAA;AAAA,IACT,UAAUC,EAAc,MAAM,SAAS,OAAA,EAAS,SAAA;AAAA,IAChD,WAAWA,EAAc,MAAM,UAAU,OAAA;AAAA,EAAO,CACjD;AACH,EACC,SAAS,OAAOC,MAAS;AACxB,QAAM,EAAE,OAAOC,EAAA,IAASD;AACxB,EAAAE,EAAO,KAAK,kCAAkC,EAAE,MAAAD,EAAA,CAAM;AAEtD,QAAME,IAAgB,MAAMC,EAAwBH,EAAK,KAAK;AAC9D,MAAI,CAACE;AACH,UAAM,IAAIE,EAAU,EAAE,MAAM,eAAe,SAAS,2BAA2B;AAEjF,MAAIF,EAAc,cAAcF,EAAK;AACnC,UAAM,IAAII,EAAU,EAAE,MAAM,aAAa,SAAS,2BAA2B;AAG/E,QAAMb,IAAU,MAAMc,EAAWH,EAAc,SAAS,GAElDI,IAAMC,EAAO,WAAA,GACbC,IAAelB,EAAYC,GAASS,EAAK,QAAQ,GAEjDS,IAAa,MAAMC,EAAiBR,EAAc,IAAIM,CAAY;AACxE,MAAIC,GAAY;AACd,QAAIA,EAAW,UAAUT,EAAK;AAC5B,YAAM,IAAII,EAAU;AAAA,QAClB,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AAGH,iBAAMO,EAAG,IAAI,mBAAmBL,CAAG,EAAE,EAAE,IAAI;AAAA,MACzC,WAAWG,EAAW;AAAA,MACtB,iBAAiBA,EAAW;AAAA,MAC5B,OAAOT,EAAK;AAAA,MACZ,MAAMA,EAAK,SAAS;AAAA,MACpB,SAASA,EAAK,SAAS;AAAA,MACvB,cAAc,KAAK,MAAM,KAAK,OAAA,IAAW,GAAI;AAAA,MAC7C,QAAQ;AAAA,MACR,WAAWY,EAAU,IAAA;AAAA,IAAI,CAC1B,GAEMC,EAAK,kBAAkBP,GAAK;AAAA,MACjC,MAAM;AAAA,MACN,WAAWG,EAAW;AAAA,MACtB,iBAAiBA,EAAW;AAAA,MAC5B,SAASA,EAAW;AAAA,IAAA,CACrB;AAAA,EACH;AAEA,QAAMK,IAAU,MAAMC,EAAiBf,EAAK,SAAS,GAE/CgB,IAAa,MAAML,EAAG,WAAW,kBAAkBT,EAAc,EAAE,WAAW,EAAE,IAAI;AAAA,IACxF,KAAAI;AAAA,IACA,UAAUN,EAAK;AAAA,IACf,cAAAQ;AAAA,IACA,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiBN,EAAc;AAAA,IAC/B,WAAWF,EAAK;AAAA,IAChB,OAAOA,EAAK;AAAA,IACZ,WAAWE,EAAc;AAAA,IACzB,YAAYA,EAAc;AAAA,IAC1B,SAASY,EAAQ;AAAA,IACjB,SAAS,CAAA;AAAA,IACT,WAAWd,EAAK;AAAA,IAChB,WAAWY,EAAU,IAAA;AAAA,IACrB,WAAWA,EAAU,IAAA;AAAA,EAAI,CAC1B;AAED,SAAOC,EAAK,kBAAkBP,GAAK;AAAA,IACjC,MAAM;AAAA,IACN,WAAWU,EAAW;AAAA,IACtB,iBAAiBd,EAAc;AAAA,IAC/B,SAAAY;AAAA,EAAA,CACD;AACH,CAAC;"}
@@ -1,58 +1,57 @@
1
- import { announcementSchema as a, contestSchema as f, participationSchema as u, studentRestoreSchema as d, variantSchema as p, studentSchema as v } from "@olinfo/quizms/models";
2
- import { validate as F } from "@olinfo/quizms/utils";
3
- import { serverTimestamp as i, Timestamp as c } from "firebase/firestore";
4
- import { cloneDeepWith as s, omit as S, isDate as h, isString as C } from "lodash-es";
5
- import { websiteSchema as D } from "../../models/website.js";
6
- function o(t) {
7
- return s(S(t, "id"), (r) => {
8
- if (h(r))
9
- return c.fromDate(r);
10
- if (C(r))
11
- return r.trim();
12
- if (r === void 0)
1
+ import { contestSchema as f, participationSchema as d, studentRestoreSchema as p, variantSchema as u, announcementSchema as F, studentSchema as S } from "@olinfo/quizms/models";
2
+ import { validate as h } from "@olinfo/quizms/utils";
3
+ import { serverTimestamp as c, Timestamp as m } from "firebase/firestore";
4
+ import { cloneDeepWith as v, omit as C, isDate as D, isString as b, transform as T, isObject as w } from "lodash-es";
5
+ import { websiteSchema as A } from "../../models/website.js";
6
+ function i(r) {
7
+ return v(C(r, "id"), (t) => {
8
+ if (D(t))
9
+ return m.fromDate(t);
10
+ if (b(t))
11
+ return t.trim();
12
+ if (t === void 0)
13
13
  return null;
14
14
  });
15
15
  }
16
- function T(t) {
17
- return s(t, (r) => {
18
- if (r instanceof c)
19
- return r.toDate();
16
+ function a(r) {
17
+ return T(r, (t, e, n) => {
18
+ e === null ? t[n] = void 0 : e instanceof m ? t[n] = e.toDate() : w(e) ? t[n] = a(e) : t[n] = e;
20
19
  });
21
20
  }
22
- function n(t, r) {
23
- const m = T({ ...r.data(), id: r.id });
24
- return F(t, m);
21
+ function s(r, t) {
22
+ const e = a({ ...t.data(), id: t.id });
23
+ return h(r, e);
25
24
  }
26
- function e(t) {
25
+ function o(r) {
27
26
  return {
28
- toFirestore: (r) => o(r),
29
- fromFirestore: (r) => n(t, r)
27
+ toFirestore: (t) => i(t),
28
+ fromFirestore: (t) => s(r, t)
30
29
  };
31
30
  }
32
- const x = e(f), W = e(u), j = e(d), k = e(p), q = e(D), y = {
33
- toFirestore(t) {
31
+ const W = o(f), q = o(d), z = o(p), B = o(u), E = o(A), G = {
32
+ toFirestore(r) {
34
33
  return {
35
- ...o(t),
36
- createdAt: i()
34
+ ...i(r),
35
+ createdAt: c()
37
36
  };
38
37
  },
39
- fromFirestore: (t) => n(a, t)
40
- }, z = {
41
- toFirestore(t) {
38
+ fromFirestore: (r) => s(F, r)
39
+ }, H = {
40
+ toFirestore(r) {
42
41
  return {
43
- ...o(t),
44
- updatedAt: i()
42
+ ...i(r),
43
+ updatedAt: c()
45
44
  };
46
45
  },
47
- fromFirestore: (t) => n(v, t)
46
+ fromFirestore: (r) => s(S, r)
48
47
  };
49
48
  export {
50
- y as announcementConverter,
51
- x as contestConverter,
52
- W as participationConverter,
53
- z as studentConverter,
54
- j as studentRestoreConvert,
55
- k as variantConverter,
56
- q as websiteConverter
49
+ G as announcementConverter,
50
+ W as contestConverter,
51
+ q as participationConverter,
52
+ H as studentConverter,
53
+ z as studentRestoreConvert,
54
+ B as variantConverter,
55
+ E as websiteConverter
57
56
  };
58
57
  //# sourceMappingURL=converters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"converters.js","sources":["../../../../src/web/common/converters.ts"],"sourcesContent":["import {\n type Announcement,\n announcementSchema,\n contestSchema,\n participationSchema,\n type Student,\n studentRestoreSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n serverTimestamp,\n Timestamp,\n} from \"firebase/firestore\";\nimport { cloneDeepWith, isDate, isString, omit } from \"lodash-es\";\nimport type z from \"zod\";\n\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: object) {\n return cloneDeepWith(omit(data, \"id\"), (value) => {\n if (isDate(value)) {\n return Timestamp.fromDate(value);\n }\n if (isString(value)) {\n return value.trim();\n }\n if (value === undefined) {\n return null;\n }\n });\n}\n\nfunction convertFromFirestore(data: object) {\n return cloneDeepWith(data, (value) => {\n if (value instanceof Timestamp) {\n return value.toDate();\n }\n });\n}\n\nfunction parse<T>(schema: z.core.$ZodType<T>, snapshot: DocumentSnapshot): T {\n const data = convertFromFirestore({ ...snapshot.data(), id: snapshot.id });\n return validate(schema, data);\n}\n\nfunction converter<T extends object>(schema: z.core.$ZodType<T>): FirestoreDataConverter<T> {\n return {\n toFirestore: (data) => convertToFirestore(data),\n fromFirestore: (snapshot) => parse(schema, snapshot),\n };\n}\n\nexport const contestConverter = converter(contestSchema);\nexport const participationConverter = converter(participationSchema);\nexport const studentRestoreConvert = converter(studentRestoreSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n\nexport const announcementConverter: FirestoreDataConverter<Announcement> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n createdAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(announcementSchema, snapshot),\n};\n\nexport const studentConverter: FirestoreDataConverter<Student> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n updatedAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(studentSchema, snapshot),\n};\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","convertFromFirestore","parse","schema","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentRestoreConvert","studentRestoreSchema","variantConverter","variantSchema","websiteConverter","websiteSchema","announcementConverter","serverTimestamp","announcementSchema","studentConverter","studentSchema"],"mappings":";;;;;AAsBA,SAASA,EAAmBC,GAAc;AACxC,SAAOC,EAAcC,EAAKF,GAAM,IAAI,GAAG,CAACG,MAAU;AAChD,QAAIC,EAAOD,CAAK;AACd,aAAOE,EAAU,SAASF,CAAK;AAEjC,QAAIG,EAASH,CAAK;AAChB,aAAOA,EAAM,KAAA;AAEf,QAAIA,MAAU;AACZ,aAAO;AAAA,EAEX,CAAC;AACH;AAEA,SAASI,EAAqBP,GAAc;AAC1C,SAAOC,EAAcD,GAAM,CAACG,MAAU;AACpC,QAAIA,aAAiBE;AACnB,aAAOF,EAAM,OAAA;AAAA,EAEjB,CAAC;AACH;AAEA,SAASK,EAASC,GAA4BC,GAA+B;AAC3E,QAAMV,IAAOO,EAAqB,EAAE,GAAGG,EAAS,QAAQ,IAAIA,EAAS,IAAI;AACzE,SAAOC,EAASF,GAAQT,CAAI;AAC9B;AAEA,SAASY,EAA4BH,GAAuD;AAC1F,SAAO;AAAA,IACL,aAAa,CAACT,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACU,MAAaF,EAAMC,GAAQC,CAAQ;AAAA,EAAA;AAEvD;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAyBH,EAAUI,CAAmB,GACtDC,IAAwBL,EAAUM,CAAoB,GACtDC,IAAmBP,EAAUQ,CAAa,GAC1CC,IAAmBT,EAAUU,CAAa,GAE1CC,IAA8D;AAAA,EACzE,YAAYvB,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWwB,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMiB,GAAoBf,CAAQ;AACjE,GAEagB,IAAoD;AAAA,EAC/D,YAAY1B,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWwB,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMmB,GAAejB,CAAQ;AAC5D;"}
1
+ {"version":3,"file":"converters.js","sources":["../../../../src/web/common/converters.ts"],"sourcesContent":["import {\n type Announcement,\n announcementSchema,\n contestSchema,\n participationSchema,\n type Student,\n studentRestoreSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n serverTimestamp,\n Timestamp,\n} from \"firebase/firestore\";\nimport { cloneDeepWith, isDate, isObject, isString, omit, transform } from \"lodash-es\";\nimport type z from \"zod\";\n\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: object) {\n return cloneDeepWith(omit(data, \"id\"), (value) => {\n if (isDate(value)) {\n return Timestamp.fromDate(value);\n }\n if (isString(value)) {\n return value.trim();\n }\n if (value === undefined) {\n return null;\n }\n });\n}\n\nfunction convertFromFirestore(data: object) {\n return transform(data, (result: any, value: any, key: string | number) => {\n if (value === null) {\n result[key] = undefined;\n } else if (value instanceof Timestamp) {\n result[key] = value.toDate();\n } else if (isObject(value)) {\n result[key] = convertFromFirestore(value);\n } else {\n result[key] = value;\n }\n });\n}\n\nfunction parse<T>(schema: z.core.$ZodType<T>, snapshot: DocumentSnapshot): T {\n const data = convertFromFirestore({ ...snapshot.data(), id: snapshot.id });\n return validate(schema, data);\n}\n\nfunction converter<T extends object>(schema: z.core.$ZodType<T>): FirestoreDataConverter<T> {\n return {\n toFirestore: (data) => convertToFirestore(data),\n fromFirestore: (snapshot) => parse(schema, snapshot),\n };\n}\n\nexport const contestConverter = converter(contestSchema);\nexport const participationConverter = converter(participationSchema);\nexport const studentRestoreConvert = converter(studentRestoreSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n\nexport const announcementConverter: FirestoreDataConverter<Announcement> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n createdAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(announcementSchema, snapshot),\n};\n\nexport const studentConverter: FirestoreDataConverter<Student> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n updatedAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(studentSchema, snapshot),\n};\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","convertFromFirestore","transform","result","key","isObject","parse","schema","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentRestoreConvert","studentRestoreSchema","variantConverter","variantSchema","websiteConverter","websiteSchema","announcementConverter","serverTimestamp","announcementSchema","studentConverter","studentSchema"],"mappings":";;;;;AAsBA,SAASA,EAAmBC,GAAc;AACxC,SAAOC,EAAcC,EAAKF,GAAM,IAAI,GAAG,CAACG,MAAU;AAChD,QAAIC,EAAOD,CAAK;AACd,aAAOE,EAAU,SAASF,CAAK;AAEjC,QAAIG,EAASH,CAAK;AAChB,aAAOA,EAAM,KAAA;AAEf,QAAIA,MAAU;AACZ,aAAO;AAAA,EAEX,CAAC;AACH;AAEA,SAASI,EAAqBP,GAAc;AAC1C,SAAOQ,EAAUR,GAAM,CAACS,GAAaN,GAAYO,MAAyB;AACxE,IAAIP,MAAU,OACZM,EAAOC,CAAG,IAAI,SACLP,aAAiBE,IAC1BI,EAAOC,CAAG,IAAIP,EAAM,OAAA,IACXQ,EAASR,CAAK,IACvBM,EAAOC,CAAG,IAAIH,EAAqBJ,CAAK,IAExCM,EAAOC,CAAG,IAAIP;AAAA,EAElB,CAAC;AACH;AAEA,SAASS,EAASC,GAA4BC,GAA+B;AAC3E,QAAMd,IAAOO,EAAqB,EAAE,GAAGO,EAAS,QAAQ,IAAIA,EAAS,IAAI;AACzE,SAAOC,EAASF,GAAQb,CAAI;AAC9B;AAEA,SAASgB,EAA4BH,GAAuD;AAC1F,SAAO;AAAA,IACL,aAAa,CAACb,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACc,MAAaF,EAAMC,GAAQC,CAAQ;AAAA,EAAA;AAEvD;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAyBH,EAAUI,CAAmB,GACtDC,IAAwBL,EAAUM,CAAoB,GACtDC,IAAmBP,EAAUQ,CAAa,GAC1CC,IAAmBT,EAAUU,CAAa,GAE1CC,IAA8D;AAAA,EACzE,YAAY3B,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAW4B,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMiB,GAAoBf,CAAQ;AACjE,GAEagB,IAAoD;AAAA,EAC/D,YAAY9B,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAW4B,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMmB,GAAejB,CAAQ;AAC5D;"}
@@ -1 +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\nexport function useCollection<T extends { id: string }>(\n path: string,\n converter: FirestoreDataConverter<T>,\n options?: QueryOption<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: false,\n revalidateOnFocus: false,\n revalidateOnMount: false,\n revalidateOnReconnect: false,\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 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?: QueryOption<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?: QueryOption<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;AAEO,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;AAAA,IACnB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,UAAU;AAAA,EAAA,GAGNC,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,YAAMK,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;"}
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\nexport function useCollection<T extends { id: string }>(\n path: string,\n converter: FirestoreDataConverter<T>,\n options?: QueryOption<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: false,\n revalidateOnFocus: false,\n revalidateOnMount: false,\n revalidateOnReconnect: false,\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 const unsubscribe = onSnapshot(\n q,\n (snap) => {\n try {\n setData(snap.docs.map((doc) => doc.data()));\n } catch (err) {\n showBoundary(err as Error);\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?: QueryOption<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?: QueryOption<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;AAEO,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;AAAA,IACnB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,UAAU;AAAA,EAAA,GAGNC,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,YAAMK,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,CAAY;AAAA,UAC3B;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;"}
@@ -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\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
+ {"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 as Error);\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,CAAY;AAAA,UAC3B;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;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olinfo/quizms-firebase",
3
- "version": "4.1.0",
3
+ "version": "4.1.2",
4
4
  "type": "module",
5
5
  "author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
6
6
  "license": "MIT",
@@ -26,40 +26,40 @@
26
26
  "dist"
27
27
  ],
28
28
  "dependencies": {
29
- "@inquirer/prompts": "^8.1.0",
30
- "@olinfo/react-components": "^0.12.0",
31
- "@trpc/client": "^11.8.0",
32
- "@trpc/server": "^11.8.0",
29
+ "@inquirer/prompts": "^8.2.1",
30
+ "@olinfo/react-components": "^0.12.1",
31
+ "@trpc/client": "^11.10.0",
32
+ "@trpc/server": "^11.10.0",
33
33
  "@types/cli-progress": "^3.11.6",
34
34
  "@types/express": "^5.0.6",
35
- "@types/express-serve-static-core": "^5.1.0",
35
+ "@types/express-serve-static-core": "^5.1.1",
36
36
  "@types/lodash-es": "^4.17.12",
37
37
  "@types/picomatch": "^4.0.2",
38
- "ag-grid-community": "^35.0.0",
38
+ "ag-grid-community": "^35.1.0",
39
39
  "cli-progress": "^3.12.0",
40
40
  "clsx": "^2.1.1",
41
- "commander": "^14.0.2",
41
+ "commander": "^14.0.3",
42
42
  "date-fns": "^4.1.0",
43
43
  "express": "^5.2.1",
44
- "fastify": "^5.6.2",
45
- "firebase": "^12.7.0",
46
- "firebase-admin": "^13.6.0",
47
- "firebase-functions": "^7.0.2",
48
- "lodash-es": "^4.17.22",
44
+ "fastify": "^5.7.4",
45
+ "firebase": "^12.9.0",
46
+ "firebase-admin": "^13.6.1",
47
+ "firebase-functions": "^7.0.5",
48
+ "lodash-es": "^4.17.23",
49
49
  "lucide-react": "^0.562.0",
50
50
  "picomatch": "^4.0.3",
51
- "react-error-boundary": "^6.0.0",
51
+ "react-error-boundary": "~6.0.3",
52
52
  "react-markdown": "^10.1.0",
53
53
  "rollup-plugin-node-externals": "^8.1.2",
54
54
  "rollup-preserve-directives": "^1.1.3",
55
55
  "superjson": "^2.2.6",
56
- "swr": "^2.3.8",
56
+ "swr": "^2.4.0",
57
57
  "tinyglobby": "^0.2.15",
58
- "vite": "^7.3.0",
58
+ "vite": "^7.3.1",
59
59
  "vite-plugin-dts": "^4.5.4",
60
60
  "wouter": "^3.9.0",
61
- "zod": "^4.2.1",
62
- "@olinfo/quizms": "^4.1.0"
61
+ "zod": "^4.3.6",
62
+ "@olinfo/quizms": "^4.1.2"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "react": "~19.1.4"
@@ -70,12 +70,12 @@
70
70
  }
71
71
  },
72
72
  "devDependencies": {
73
- "@biomejs/biome": "^2.3.10",
74
- "@google-cloud/storage": "^7.18.0",
75
- "@types/react": "^19.2.7",
73
+ "@biomejs/biome": "^2.4.2",
74
+ "@google-cloud/storage": "^7.19.0",
75
+ "@types/react": "^19.2.14",
76
76
  "@types/wicg-file-system-access": "^2023.10.7",
77
77
  "typescript": "5.8.2",
78
- "vite-tsconfig-paths": "^6.0.3"
78
+ "vite-tsconfig-paths": "^6.1.1"
79
79
  },
80
80
  "scripts": {
81
81
  "prebuild": "biome check && tsc --noEmit",