@olinfo/quizms-firebase 4.0.0 → 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 +28 -0
  21. package/dist/src/functions/api/admin.js.map +1 -0
  22. package/dist/src/functions/api/index.js +26 -34
  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,19 +1,10 @@
1
- import { CallableRequest } from 'firebase-functions/https';
2
1
  import { Change } from 'firebase-functions/firestore';
3
2
  import { DocumentSnapshot } from 'firebase-admin/firestore';
3
+ import { Express as Express_2 } from 'express-serve-static-core';
4
4
  import { FirestoreAuthEvent } from 'firebase-functions/firestore';
5
5
  import { Request as Request_2 } from 'firebase-functions/tasks';
6
6
 
7
- export declare function apiHandler(request: CallableRequest): Promise<EndpointSuccess<any> | {
8
- success: boolean;
9
- errorCode: string;
10
- error: string;
11
- }>;
12
-
13
- declare type EndpointSuccess<Res> = {
14
- success: true;
15
- data: Res;
16
- };
7
+ export declare const apiHandler: Express_2;
17
8
 
18
9
  export declare function studentUpdatedHandler(event: FirestoreAuthEvent<Change<DocumentSnapshot> | undefined>): Promise<void>;
19
10
 
package/dist/functions.js CHANGED
@@ -1,8 +1,8 @@
1
- import { apiHandler as d } from "./src/functions/api/index.js";
1
+ import { default as a } from "./src/functions/api/index.js";
2
2
  import { studentUpdatedHandler as t } from "./src/functions/submissions.js";
3
3
  import { updateScoresHandler as p } from "./src/functions/update-scores.js";
4
4
  export {
5
- d as apiHandler,
5
+ a as apiHandler,
6
6
  t as studentUpdatedHandler,
7
7
  p as updateScoresHandler
8
8
  };
@@ -1,4 +1,4 @@
1
- const e = "4.0.0", n = { "firebase-functions": "^7.0.0" }, s = {
1
+ const e = "4.1.0", n = { "firebase-functions": "^7.0.2" }, s = {
2
2
  version: e,
3
3
  dependencies: n
4
4
  };
@@ -1,16 +1,16 @@
1
- const n = `import {
1
+ const t = `import {
2
2
  apiHandler,
3
3
  studentUpdatedHandler,
4
4
  updateScoresHandler,
5
5
  } from "@olinfo/quizms-firebase/functions";
6
6
  import { setGlobalOptions } from "firebase-functions";
7
7
  import { onDocumentWrittenWithAuthContext } from "firebase-functions/firestore";
8
- import { onCall } from "firebase-functions/https";
8
+ import { onRequest } from "firebase-functions/https";
9
9
  import { onTaskDispatched } from "firebase-functions/tasks";
10
10
 
11
11
  setGlobalOptions({ region: "QUIZMS_REGION" });
12
12
 
13
- export const api = onCall({ cors: ["http://localhost:1234"] }, apiHandler);
13
+ export const api = onRequest({ cors: ["http://localhost:1234"] }, apiHandler);
14
14
 
15
15
  export const submissions = onDocumentWrittenWithAuthContext(
16
16
  "participations/{participationId}/students/{studentId}",
@@ -20,6 +20,6 @@ export const submissions = onDocumentWrittenWithAuthContext(
20
20
  export const updateScores = onTaskDispatched({ timeoutSeconds: 540 }, updateScoresHandler);
21
21
  `;
22
22
  export {
23
- n as default
23
+ t as default
24
24
  };
25
25
  //# sourceMappingURL=functions-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"functions-index.js","sources":["../../../../src/cli/files/functions-index.js?raw"],"sourcesContent":["export default \"import {\\n apiHandler,\\n studentUpdatedHandler,\\n updateScoresHandler,\\n} from \\\"@olinfo/quizms-firebase/functions\\\";\\nimport { setGlobalOptions } from \\\"firebase-functions\\\";\\nimport { onDocumentWrittenWithAuthContext } from \\\"firebase-functions/firestore\\\";\\nimport { onCall } from \\\"firebase-functions/https\\\";\\nimport { onTaskDispatched } from \\\"firebase-functions/tasks\\\";\\n\\nsetGlobalOptions({ region: \\\"QUIZMS_REGION\\\" });\\n\\nexport const api = onCall({ cors: [\\\"http://localhost:1234\\\"] }, apiHandler);\\n\\nexport const submissions = onDocumentWrittenWithAuthContext(\\n \\\"participations/{participationId}/students/{studentId}\\\",\\n studentUpdatedHandler,\\n);\\n\\nexport const updateScores = onTaskDispatched({ timeoutSeconds: 540 }, updateScoresHandler);\\n\""],"names":["functionsIndex"],"mappings":"AAAA,MAAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}
1
+ {"version":3,"file":"functions-index.js","sources":["../../../../src/cli/files/functions-index.js?raw"],"sourcesContent":["export default \"import {\\n apiHandler,\\n studentUpdatedHandler,\\n updateScoresHandler,\\n} from \\\"@olinfo/quizms-firebase/functions\\\";\\nimport { setGlobalOptions } from \\\"firebase-functions\\\";\\nimport { onDocumentWrittenWithAuthContext } from \\\"firebase-functions/firestore\\\";\\nimport { onRequest } from \\\"firebase-functions/https\\\";\\nimport { onTaskDispatched } from \\\"firebase-functions/tasks\\\";\\n\\nsetGlobalOptions({ region: \\\"QUIZMS_REGION\\\" });\\n\\nexport const api = onRequest({ cors: [\\\"http://localhost:1234\\\"] }, apiHandler);\\n\\nexport const submissions = onDocumentWrittenWithAuthContext(\\n \\\"participations/{participationId}/students/{studentId}\\\",\\n studentUpdatedHandler,\\n);\\n\\nexport const updateScores = onTaskDispatched({ timeoutSeconds: 540 }, updateScoresHandler);\\n\""],"names":["functionsIndex"],"mappings":"AAAA,MAAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}
@@ -1,10 +1,10 @@
1
1
  import { writeFile as d, mkdir as p, readFile as g } from "node:fs/promises";
2
2
  import n from "node:path";
3
3
  import { cwd as l } from "node:process";
4
- import { select as m, confirm as y } from "@inquirer/prompts";
4
+ import { styleText as y } from "node:util";
5
+ import { select as m, confirm as w } from "@inquirer/prompts";
5
6
  import { success as c, error as f, info as o } from "@olinfo/quizms/utils-node";
6
- import { applicationDefault as w } from "firebase-admin/app";
7
- import h from "picocolors";
7
+ import { applicationDefault as h } from "firebase-admin/app";
8
8
  import { getGcpRegions as k } from "./utils/region-finder.js";
9
9
  import v from "./files/firestore.rules.js";
10
10
  import S from "./files/firestore-indexes.json.js";
@@ -22,7 +22,7 @@ async function Y(e) {
22
22
  await $(a) && (i = !1), await D(s) && (i = !1), i ? c("Project initialized!") : f("The initialization was not completed due to some previous errors.");
23
23
  }
24
24
  async function I() {
25
- const e = await j(w(), "firebase", "v1beta1", ""), i = await m({
25
+ const e = await j(h(), "firebase", "v1beta1", ""), i = await m({
26
26
  message: "Seleziona il progetto Firebase",
27
27
  choices: e.results.map((t) => ({
28
28
  value: t.projectId,
@@ -131,10 +131,10 @@ async function $(e) {
131
131
  "v1",
132
132
  "/databases/(default)/backupSchedules"
133
133
  );
134
- return i.backupSchedules.some((s) => s.dailyRecurrence) ? o("Daily backup schedule already exists.") : await u(e, "firestore", "v1", "/databases/(default)/backupSchedules", {
134
+ return i.backupSchedules?.some((s) => s.dailyRecurrence) ? o("Daily backup schedule already exists.") : await u(e, "firestore", "v1", "/databases/(default)/backupSchedules", {
135
135
  dailyRecurrence: {},
136
136
  retention: "604800s"
137
- }), i.backupSchedules.some((s) => s.weeklyRecurrence) ? o("Weekly backup schedule already exists.") : await u(e, "firestore", "v1", "/databases/(default)/backupSchedules", {
137
+ }), i.backupSchedules?.some((s) => s.weeklyRecurrence) ? o("Weekly backup schedule already exists.") : await u(e, "firestore", "v1", "/databases/(default)/backupSchedules", {
138
138
  weeklyRecurrence: {
139
139
  day: "SUNDAY"
140
140
  },
@@ -166,8 +166,8 @@ async function r(e, i, t) {
166
166
  } catch {
167
167
  a = !0;
168
168
  }
169
- a || (a = await y({
170
- message: `The file ${h.bold(n.basename(e))} already exists. Do you want to overwrite it?`
169
+ a || (a = await w({
170
+ message: `The file ${y("bold", n.basename(e))} already exists. Do you want to overwrite it?`
171
171
  })), a && await d(e, i);
172
172
  }
173
173
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sources":["../../../src/cli/init.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { cwd } from \"node:process\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport { error, info, success } from \"@olinfo/quizms/utils-node\";\nimport { type App, applicationDefault } from \"firebase-admin/app\";\nimport pc from \"picocolors\";\n\nimport { getGcpRegions } from \"~/cli/utils/region-finder\";\n\nimport firestoreRules from \"./files/firestore.rules?raw\";\nimport firestoreIndexes from \"./files/firestore-indexes.json\";\nimport functionsIndex from \"./files/functions-index.js?raw\";\nimport functionsPackage from \"./files/functions-package\";\nimport storageRules from \"./files/storage.rules?raw\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { restApi, restProjectApi } from \"./utils/rest-api\";\n\ntype InitOptions = {\n force?: boolean;\n};\n\nexport default async function init(options: InitOptions) {\n let initialized = true;\n\n await selectProject();\n const region = await getRegion();\n\n if (await copyFiles(options, region)) initialized = false;\n\n const { app, bucket } = await initializeFirebase();\n if (await enableBackups(app)) initialized = false;\n if (await enableCors(bucket)) initialized = false;\n\n if (initialized) {\n success(\"Project initialized!\");\n } else {\n error(\"The initialization was not completed due to some previous errors.\");\n }\n}\n\nasync function selectProject() {\n const data = await restApi(applicationDefault(), \"firebase\", \"v1beta1\", \"\");\n\n const answer = await select<string>({\n message: \"Seleziona il progetto Firebase\",\n choices: data.results.map((project: any) => ({\n value: project.projectId,\n name: project.displayName,\n })),\n });\n await writeFile(\".firebaserc\", JSON.stringify({ projects: { default: answer } }, null, 2));\n}\n\nasync function getRegion() {\n const regions = await getGcpRegions();\n return select<string>({\n message: \"Seleziona la regione\",\n choices: regions.map((region) => ({\n value: region.region,\n name: `${region.region} (${region.name})`,\n })),\n });\n}\n\nasync function copyFiles(options: InitOptions, region: string) {\n info(\"Copying files...\");\n\n await mkdir(\"firebase/functions\", { recursive: true });\n\n const firestoreRulesPath = \"firebase/firestore.rules\";\n await initFile(firestoreRulesPath, firestoreRules, options);\n\n const firestoreIndexesPath = \"firebase/firestore-indexes.json\";\n await initFile(firestoreIndexesPath, JSON.stringify(firestoreIndexes, undefined, 2), options);\n\n const storageRulesPath = \"firebase/storage.rules\";\n await initFile(storageRulesPath, storageRules, options);\n\n await initFile(\"firebase/functions/.env\", `QUIZMS_REGION=${region}`, options);\n await initFile(\n \"firebase/functions/index.js\",\n functionsIndex.replace(\"QUIZMS_REGION\", region),\n options,\n );\n await initFile(\n \"firebase/functions/package.json\",\n JSON.stringify(functionsPackage, undefined, 2),\n options,\n );\n\n const configs = {\n $schema: \"./node_modules/firebase-tools/schema/firebase-config.json\",\n hosting: hostingConfigs(region),\n firestore: {\n rules: path.relative(cwd(), firestoreRulesPath),\n indexes: path.relative(cwd(), firestoreIndexesPath),\n },\n storage: {\n rules: path.relative(cwd(), storageRulesPath),\n },\n functions: [\n {\n codebase: \"default\",\n disallowLegacyRuntimeConfig: true,\n ignore: [\"node_modules\", \"*.log\"],\n source: \"firebase/functions\",\n runtime: \"nodejs24\",\n },\n ],\n };\n await initFile(\"firebase.json\", JSON.stringify(configs, undefined, 2), options);\n\n success(\"Files copied!\");\n return 0;\n}\n\nfunction hostingConfigs(region: string) {\n return {\n public: \".quizms/hosting-build\",\n ignore: [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n rewrites: [\n {\n source: \"/api\",\n function: {\n functionId: \"api\",\n region,\n },\n },\n {\n source: \"**\",\n destination: \"/index.html\",\n },\n ],\n trailingSlash: false,\n headers: [\n {\n source: \"/assets/**\",\n headers: [\n {\n key: \"Cache-Control\",\n value: \"public, max-age=31536000, immutable\", // 365 days\n },\n ],\n },\n {\n source: \"/blockly/**\",\n headers: [\n {\n key: \"Cache-Control\",\n value: \"public, max-age=86400\", // 1 day\n },\n ],\n },\n ],\n predeploy: \"npx quizms build firebase\",\n };\n}\n\nasync function enableBackups(app: App) {\n info(\"Enabling Firestore backups...\");\n\n try {\n const existingsBackups = await restProjectApi(\n app,\n \"firestore\",\n \"v1\",\n \"/databases/(default)/backupSchedules\",\n );\n const hasDaily = existingsBackups.backupSchedules.some((s: any) => s.dailyRecurrence);\n if (hasDaily) {\n info(\"Daily backup schedule already exists.\");\n } else {\n await restProjectApi(app, \"firestore\", \"v1\", \"/databases/(default)/backupSchedules\", {\n dailyRecurrence: {},\n retention: \"604800s\",\n });\n }\n\n const hasWeekly = existingsBackups.backupSchedules.some((s: any) => s.weeklyRecurrence);\n if (hasWeekly) {\n info(\"Weekly backup schedule already exists.\");\n } else {\n await restProjectApi(app, \"firestore\", \"v1\", \"/databases/(default)/backupSchedules\", {\n weeklyRecurrence: {\n day: \"SUNDAY\",\n },\n retention: \"8467200s\",\n });\n }\n\n success(\"Backups enabled!\");\n return 0;\n } catch (err) {\n error(`Failed to enable Firestore backups: ${err}`);\n return 1;\n }\n}\n\nasync function enableCors(bucket: Bucket) {\n info(\"Enabling CORS for Firebase bucket...\");\n\n try {\n await bucket.setCorsConfiguration([\n {\n origin: [\"*\"],\n method: [\"GET\"],\n maxAgeSeconds: 3600,\n },\n ]);\n success(\"CORS enabled!\");\n return 0;\n } catch (err) {\n error(`Failed to set CORS configuration: ${err}`);\n return 1;\n }\n}\n\nasync function initFile(fileName: string, content: string, options?: InitOptions) {\n let write = options?.force ?? false;\n if (!write) {\n try {\n const prevContent = await readFile(fileName, \"utf8\");\n if (prevContent === content) return;\n } catch {\n write = true;\n }\n }\n\n if (!write) {\n write = await confirm({\n message: `The file ${pc.bold(path.basename(fileName))} already exists. Do you want to overwrite it?`,\n });\n }\n\n if (write) {\n await writeFile(fileName, content);\n }\n}\n"],"names":["init","options","initialized","selectProject","region","getRegion","copyFiles","app","bucket","initializeFirebase","enableBackups","enableCors","success","error","data","restApi","applicationDefault","answer","select","project","writeFile","regions","getGcpRegions","info","mkdir","firestoreRulesPath","initFile","firestoreRules","firestoreIndexesPath","firestoreIndexes","storageRulesPath","storageRules","functionsIndex","functionsPackage","configs","hostingConfigs","path","cwd","existingsBackups","restProjectApi","err","fileName","content","write","readFile","confirm","pc"],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sources":["../../../src/cli/init.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { cwd } from \"node:process\";\nimport { styleText } from \"node:util\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport { error, info, success } from \"@olinfo/quizms/utils-node\";\nimport { type App, applicationDefault } from \"firebase-admin/app\";\n\nimport { getGcpRegions } from \"~/cli/utils/region-finder\";\n\nimport firestoreRules from \"./files/firestore.rules?raw\";\nimport firestoreIndexes from \"./files/firestore-indexes.json\";\nimport functionsIndex from \"./files/functions-index.js?raw\";\nimport functionsPackage from \"./files/functions-package\";\nimport storageRules from \"./files/storage.rules?raw\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { restApi, restProjectApi } from \"./utils/rest-api\";\n\ntype InitOptions = {\n force?: boolean;\n};\n\nexport default async function init(options: InitOptions) {\n let initialized = true;\n\n await selectProject();\n const region = await getRegion();\n\n if (await copyFiles(options, region)) initialized = false;\n\n const { app, bucket } = await initializeFirebase();\n if (await enableBackups(app)) initialized = false;\n if (await enableCors(bucket)) initialized = false;\n\n if (initialized) {\n success(\"Project initialized!\");\n } else {\n error(\"The initialization was not completed due to some previous errors.\");\n }\n}\n\nasync function selectProject() {\n const data = await restApi(applicationDefault(), \"firebase\", \"v1beta1\", \"\");\n\n const answer = await select<string>({\n message: \"Seleziona il progetto Firebase\",\n choices: data.results.map((project: any) => ({\n value: project.projectId,\n name: project.displayName,\n })),\n });\n await writeFile(\".firebaserc\", JSON.stringify({ projects: { default: answer } }, null, 2));\n}\n\nasync function getRegion() {\n const regions = await getGcpRegions();\n return select<string>({\n message: \"Seleziona la regione\",\n choices: regions.map((region) => ({\n value: region.region,\n name: `${region.region} (${region.name})`,\n })),\n });\n}\n\nasync function copyFiles(options: InitOptions, region: string) {\n info(\"Copying files...\");\n\n await mkdir(\"firebase/functions\", { recursive: true });\n\n const firestoreRulesPath = \"firebase/firestore.rules\";\n await initFile(firestoreRulesPath, firestoreRules, options);\n\n const firestoreIndexesPath = \"firebase/firestore-indexes.json\";\n await initFile(firestoreIndexesPath, JSON.stringify(firestoreIndexes, undefined, 2), options);\n\n const storageRulesPath = \"firebase/storage.rules\";\n await initFile(storageRulesPath, storageRules, options);\n\n await initFile(\"firebase/functions/.env\", `QUIZMS_REGION=${region}`, options);\n await initFile(\n \"firebase/functions/index.js\",\n functionsIndex.replace(\"QUIZMS_REGION\", region),\n options,\n );\n await initFile(\n \"firebase/functions/package.json\",\n JSON.stringify(functionsPackage, undefined, 2),\n options,\n );\n\n const configs = {\n $schema: \"./node_modules/firebase-tools/schema/firebase-config.json\",\n hosting: hostingConfigs(region),\n firestore: {\n rules: path.relative(cwd(), firestoreRulesPath),\n indexes: path.relative(cwd(), firestoreIndexesPath),\n },\n storage: {\n rules: path.relative(cwd(), storageRulesPath),\n },\n functions: [\n {\n codebase: \"default\",\n disallowLegacyRuntimeConfig: true,\n ignore: [\"node_modules\", \"*.log\"],\n source: \"firebase/functions\",\n runtime: \"nodejs24\",\n },\n ],\n };\n await initFile(\"firebase.json\", JSON.stringify(configs, undefined, 2), options);\n\n success(\"Files copied!\");\n return 0;\n}\n\nfunction hostingConfigs(region: string) {\n return {\n public: \".quizms/hosting-build\",\n ignore: [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n rewrites: [\n {\n source: \"/api\",\n function: {\n functionId: \"api\",\n region,\n },\n },\n {\n source: \"**\",\n destination: \"/index.html\",\n },\n ],\n trailingSlash: false,\n headers: [\n {\n source: \"/assets/**\",\n headers: [\n {\n key: \"Cache-Control\",\n value: \"public, max-age=31536000, immutable\", // 365 days\n },\n ],\n },\n {\n source: \"/blockly/**\",\n headers: [\n {\n key: \"Cache-Control\",\n value: \"public, max-age=86400\", // 1 day\n },\n ],\n },\n ],\n predeploy: \"npx quizms build firebase\",\n };\n}\n\nasync function enableBackups(app: App) {\n info(\"Enabling Firestore backups...\");\n\n try {\n const existingsBackups = await restProjectApi(\n app,\n \"firestore\",\n \"v1\",\n \"/databases/(default)/backupSchedules\",\n );\n const hasDaily = existingsBackups.backupSchedules?.some((s: any) => s.dailyRecurrence);\n if (hasDaily) {\n info(\"Daily backup schedule already exists.\");\n } else {\n await restProjectApi(app, \"firestore\", \"v1\", \"/databases/(default)/backupSchedules\", {\n dailyRecurrence: {},\n retention: \"604800s\",\n });\n }\n\n const hasWeekly = existingsBackups.backupSchedules?.some((s: any) => s.weeklyRecurrence);\n if (hasWeekly) {\n info(\"Weekly backup schedule already exists.\");\n } else {\n await restProjectApi(app, \"firestore\", \"v1\", \"/databases/(default)/backupSchedules\", {\n weeklyRecurrence: {\n day: \"SUNDAY\",\n },\n retention: \"8467200s\",\n });\n }\n\n success(\"Backups enabled!\");\n return 0;\n } catch (err) {\n error(`Failed to enable Firestore backups: ${err}`);\n return 1;\n }\n}\n\nasync function enableCors(bucket: Bucket) {\n info(\"Enabling CORS for Firebase bucket...\");\n\n try {\n await bucket.setCorsConfiguration([\n {\n origin: [\"*\"],\n method: [\"GET\"],\n maxAgeSeconds: 3600,\n },\n ]);\n success(\"CORS enabled!\");\n return 0;\n } catch (err) {\n error(`Failed to set CORS configuration: ${err}`);\n return 1;\n }\n}\n\nasync function initFile(fileName: string, content: string, options?: InitOptions) {\n let write = options?.force ?? false;\n if (!write) {\n try {\n const prevContent = await readFile(fileName, \"utf8\");\n if (prevContent === content) return;\n } catch {\n write = true;\n }\n }\n\n if (!write) {\n write = await confirm({\n message: `The file ${styleText(\"bold\", path.basename(fileName))} already exists. Do you want to overwrite it?`,\n });\n }\n\n if (write) {\n await writeFile(fileName, content);\n }\n}\n"],"names":["init","options","initialized","selectProject","region","getRegion","copyFiles","app","bucket","initializeFirebase","enableBackups","enableCors","success","error","data","restApi","applicationDefault","answer","select","project","writeFile","regions","getGcpRegions","info","mkdir","firestoreRulesPath","initFile","firestoreRules","firestoreIndexesPath","firestoreIndexes","storageRulesPath","storageRules","functionsIndex","functionsPackage","configs","hostingConfigs","path","cwd","existingsBackups","restProjectApi","err","fileName","content","write","readFile","confirm","styleText"],"mappings":"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"}
@@ -1,37 +1,26 @@
1
- import { pipeline as d } from "node:stream/promises";
2
- import { calcScore as l } from "@olinfo/quizms/models";
3
- import { info as v } from "@olinfo/quizms/utils-node";
4
- import { SingleBar as w } from "cli-progress";
5
- import { formatDistanceStrict as h } from "date-fns";
6
- import { variantConverter as b, studentConverter as C } from "./utils/converters-admin.js";
7
- import { initializeFirebase as S } from "./utils/initialize.js";
8
- async function D() {
9
- const { db: n } = await S();
10
- v("Updating student scores.");
11
- const m = await n.collection("variants").withConverter(b).get(), f = Object.fromEntries(m.docs.map((t) => [t.id, t.data()])), e = n.collectionGroup("students").withConverter(C), p = await e.count().get(), r = new w({
12
- format: " {bar} {percentage}% | ETA: {eta_formatted} | {value}/{total}",
13
- barCompleteChar: "█",
14
- barIncompleteChar: "▂",
15
- etaBuffer: 1e4,
16
- formatTime: (t) => h(0, t * 1e3)
17
- });
18
- r.start(p.data().count, 0), await d(e.stream(), async function* (t) {
19
- const o = [];
20
- for await (const u of t) {
21
- const i = u.ref;
22
- o.push(
23
- n.runTransaction(async (s) => {
24
- const a = (await s.get(i)).data();
25
- if (!a?.variant) return;
26
- const c = l(a, f[a.variant]?.schema) ?? null;
27
- c !== a.score && s.update(i, { score: c });
28
- }).then(() => r.increment())
29
- );
1
+ import { calcScore as d } from "@olinfo/quizms/models";
2
+ import { info as f, withProgress as p } from "@olinfo/quizms/utils-node";
3
+ import { variantConverter as m, studentConverter as v } from "./utils/converters-admin.js";
4
+ import { initializeFirebase as l } from "./utils/initialize.js";
5
+ async function y() {
6
+ const { db: n } = await l();
7
+ f("Updating student scores.");
8
+ const s = await n.collection("variants").withConverter(m).get(), c = Object.fromEntries(s.docs.map((t) => [t.id, t.data()])), r = n.collectionGroup("students").withConverter(v), u = await r.count().get();
9
+ await p(
10
+ r.stream(),
11
+ u.data().count,
12
+ async (t) => {
13
+ const e = t.ref;
14
+ await n.runTransaction(async (o) => {
15
+ const a = (await o.get(e)).data();
16
+ if (!a?.variant) return;
17
+ const i = d(a, c[a.variant]?.schema) ?? null;
18
+ i !== a.score && o.update(e, { score: i });
19
+ });
30
20
  }
31
- await Promise.all(o);
32
- }), r.stop();
21
+ );
33
22
  }
34
23
  export {
35
- D as default
24
+ y as default
36
25
  };
37
26
  //# sourceMappingURL=update-scores.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-scores.js","sources":["../../../src/cli/update-scores.ts"],"sourcesContent":["import { pipeline } from \"node:stream/promises\";\n\nimport { calcScore, type Student } from \"@olinfo/quizms/models\";\nimport { info } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport { formatDistanceStrict } from \"date-fns\";\nimport type { QueryDocumentSnapshot } from \"firebase-admin/firestore\";\n\nimport { studentConverter, variantConverter } from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\n\nexport default async function updateScores() {\n const { db } = await initializeFirebase();\n\n info(\"Updating student scores.\");\n\n const variantRef = db.collection(\"variants\").withConverter(variantConverter);\n const variantSnap = await variantRef.get();\n const variants = Object.fromEntries(variantSnap.docs.map((doc) => [doc.id, doc.data()]));\n\n const ref = db.collectionGroup(\"students\").withConverter(studentConverter);\n\n const count = await ref.count().get();\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | ETA: {eta_formatted} | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n etaBuffer: 10000,\n formatTime: (t) => formatDistanceStrict(0, t * 1000),\n });\n\n bar.start(count.data().count, 0);\n await pipeline(ref.stream(), async function* (stream) {\n const promises: Promise<void>[] = [];\n for await (const data of stream) {\n const snapshot = data as unknown as QueryDocumentSnapshot<Student>;\n const ref = snapshot.ref;\n promises.push(\n db\n .runTransaction(async (t) => {\n const studentSnap = await t.get(ref);\n const student = studentSnap.data();\n if (!student?.variant) return;\n const score = calcScore(student, variants[student.variant]?.schema) ?? null;\n if (score !== student.score) {\n t.update(ref, { score });\n }\n })\n .then(() => bar.increment()),\n );\n }\n await Promise.all(promises);\n });\n bar.stop();\n}\n"],"names":["updateScores","db","initializeFirebase","info","variantSnap","variantConverter","variants","doc","ref","studentConverter","count","bar","SingleBar","formatDistanceStrict","pipeline","stream","promises","data","t","student","score","calcScore"],"mappings":";;;;;;;AAWA,eAA8BA,IAAe;AAC3C,QAAM,EAAE,IAAAC,MAAO,MAAMC,EAAA;AAErB,EAAAC,EAAK,0BAA0B;AAG/B,QAAMC,IAAc,MADDH,EAAG,WAAW,UAAU,EAAE,cAAcI,CAAgB,EACtC,IAAA,GAC/BC,IAAW,OAAO,YAAYF,EAAY,KAAK,IAAI,CAACG,MAAQ,CAACA,EAAI,IAAIA,EAAI,KAAA,CAAM,CAAC,CAAC,GAEjFC,IAAMP,EAAG,gBAAgB,UAAU,EAAE,cAAcQ,CAAgB,GAEnEC,IAAQ,MAAMF,EAAI,MAAA,EAAQ,IAAA,GAC1BG,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,YAAY,CAAC,MAAMC,EAAqB,GAAG,IAAI,GAAI;AAAA,EAAA,CACpD;AAED,EAAAF,EAAI,MAAMD,EAAM,KAAA,EAAO,OAAO,CAAC,GAC/B,MAAMI,EAASN,EAAI,OAAA,GAAU,iBAAiBO,GAAQ;AACpD,UAAMC,IAA4B,CAAA;AAClC,qBAAiBC,KAAQF,GAAQ;AAE/B,YAAMP,IADWS,EACI;AACrB,MAAAD,EAAS;AAAA,QACPf,EACG,eAAe,OAAOiB,MAAM;AAE3B,gBAAMC,KADc,MAAMD,EAAE,IAAIV,CAAG,GACP,KAAA;AAC5B,cAAI,CAACW,GAAS,QAAS;AACvB,gBAAMC,IAAQC,EAAUF,GAASb,EAASa,EAAQ,OAAO,GAAG,MAAM,KAAK;AACvE,UAAIC,MAAUD,EAAQ,SACpBD,EAAE,OAAOV,GAAK,EAAE,OAAAY,EAAA,CAAO;AAAA,QAE3B,CAAC,EACA,KAAK,MAAMT,EAAI,WAAW;AAAA,MAAA;AAAA,IAEjC;AACA,UAAM,QAAQ,IAAIK,CAAQ;AAAA,EAC5B,CAAC,GACDL,EAAI,KAAA;AACN;"}
1
+ {"version":3,"file":"update-scores.js","sources":["../../../src/cli/update-scores.ts"],"sourcesContent":["import { calcScore, type Student } from \"@olinfo/quizms/models\";\nimport { info, withProgress } from \"@olinfo/quizms/utils-node\";\nimport type { QueryDocumentSnapshot } from \"firebase-admin/firestore\";\n\nimport { studentConverter, variantConverter } from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\n\nexport default async function updateScores() {\n const { db } = await initializeFirebase();\n\n info(\"Updating student scores.\");\n\n const variantRef = db.collection(\"variants\").withConverter(variantConverter);\n const variantSnap = await variantRef.get();\n const variants = Object.fromEntries(variantSnap.docs.map((doc) => [doc.id, doc.data()]));\n\n const ref = db.collectionGroup(\"students\").withConverter(studentConverter);\n\n const count = await ref.count().get();\n await withProgress(\n ref.stream() as AsyncIterable<QueryDocumentSnapshot<Student>>,\n count.data().count,\n async (snapshot) => {\n const ref = snapshot.ref;\n await db.runTransaction(async (t) => {\n const studentSnap = await t.get(ref);\n const student = studentSnap.data();\n if (!student?.variant) return;\n const score = calcScore(student, variants[student.variant]?.schema) ?? null;\n if (score !== student.score) {\n t.update(ref, { score });\n }\n });\n },\n );\n}\n"],"names":["updateScores","db","initializeFirebase","info","variantSnap","variantConverter","variants","doc","ref","studentConverter","count","withProgress","snapshot","t","student","score","calcScore"],"mappings":";;;;AAOA,eAA8BA,IAAe;AAC3C,QAAM,EAAE,IAAAC,MAAO,MAAMC,EAAA;AAErB,EAAAC,EAAK,0BAA0B;AAG/B,QAAMC,IAAc,MADDH,EAAG,WAAW,UAAU,EAAE,cAAcI,CAAgB,EACtC,IAAA,GAC/BC,IAAW,OAAO,YAAYF,EAAY,KAAK,IAAI,CAACG,MAAQ,CAACA,EAAI,IAAIA,EAAI,KAAA,CAAM,CAAC,CAAC,GAEjFC,IAAMP,EAAG,gBAAgB,UAAU,EAAE,cAAcQ,CAAgB,GAEnEC,IAAQ,MAAMF,EAAI,MAAA,EAAQ,IAAA;AAChC,QAAMG;AAAA,IACJH,EAAI,OAAA;AAAA,IACJE,EAAM,OAAO;AAAA,IACb,OAAOE,MAAa;AAClB,YAAMJ,IAAMI,EAAS;AACrB,YAAMX,EAAG,eAAe,OAAOY,MAAM;AAEnC,cAAMC,KADc,MAAMD,EAAE,IAAIL,CAAG,GACP,KAAA;AAC5B,YAAI,CAACM,GAAS,QAAS;AACvB,cAAMC,IAAQC,EAAUF,GAASR,EAASQ,EAAQ,OAAO,GAAG,MAAM,KAAK;AACvE,QAAIC,MAAUD,EAAQ,SACpBD,EAAE,OAAOL,GAAK,EAAE,OAAAO,EAAA,CAAO;AAAA,MAE3B,CAAC;AAAA,IACH;AAAA,EAAA;AAEJ;"}
@@ -1,37 +1,39 @@
1
- import { confirm as h } from "@inquirer/prompts";
1
+ import { styleText as h } from "node:util";
2
+ import { confirm as g } from "@inquirer/prompts";
2
3
  import { fatal as s, info as $, success as b } from "@olinfo/quizms/utils-node";
3
- import { SingleBar as C } from "cli-progress";
4
- import { upperFirst as x, partition as y } from "lodash-es";
5
- import g from "picocolors";
4
+ import { SingleBar as x } from "cli-progress";
5
+ import { upperFirst as y, partition as C } from "lodash-es";
6
6
  async function E(o, t, m, w, r) {
7
- const c = /* @__PURE__ */ new Set();
7
+ const l = /* @__PURE__ */ new Set();
8
8
  for (const e of m)
9
- c.has(e.id) && s(`Cannot import multiple ${t} with the same id: ${e.id}`), c.add(e.id);
9
+ l.has(e.id) && s(`Cannot import multiple ${t} with the same id: ${e.id}`), l.add(e.id);
10
10
  if (r.delete) {
11
- await h({
12
- message: `You are about to delete all ${g.bold(
11
+ await g({
12
+ message: `You are about to delete all ${h(
13
+ "bold",
13
14
  t
14
15
  )}. Any previous data will be lost, this operation cannot be undone. Are you really sure?`,
15
16
  default: !1
16
17
  }) || s("Command aborted.");
17
18
  const a = o.collection(t);
18
- await o.recursiveDelete(a), $(`${x(t)} deleted!`);
19
+ await o.recursiveDelete(a), $(`${y(t)} deleted!`);
19
20
  }
20
- const l = /* @__PURE__ */ new Set();
21
+ const c = /* @__PURE__ */ new Set();
21
22
  for (const e of await o.collection(t).listDocuments())
22
- l.add(e.id);
23
- const [f, p] = y(m, (e) => l.has(e.id));
23
+ c.add(e.id);
24
+ const [f, u] = C(m, (e) => c.has(e.id));
24
25
  f.length > 0 && !r.skipExisting && !r.force && s(
25
26
  `Documents already exist in \`${t}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
26
27
  );
27
- const i = r.skipExisting ? p : [...f, ...p];
28
- r.force && f.length > 0 && (await h({
29
- message: `You are about to import the ${g.bold(
28
+ const i = r.skipExisting ? u : [...f, ...u];
29
+ r.force && f.length > 0 && (await g({
30
+ message: `You are about to import the ${h(
31
+ "bold",
30
32
  t
31
33
  )}. ${f.length} document will be overwritten, the previous data will be lost. Are you really sure?`,
32
34
  default: !1
33
35
  }) || s("Command aborted."));
34
- const d = new C({
36
+ const d = new x({
35
37
  format: " {bar} {percentage}% | {value}/{total}",
36
38
  barCompleteChar: "█",
37
39
  barIncompleteChar: "▂"
@@ -41,8 +43,8 @@ async function E(o, t, m, w, r) {
41
43
  d.update(e);
42
44
  const a = o.batch();
43
45
  for (const n of i.slice(e, e + 400)) {
44
- const u = o.doc(`${t}/${n.id}`).withConverter(w);
45
- r.force ? a.set(u, n) : a.create(u, n);
46
+ const p = o.doc(`${t}/${n.id}`).withConverter(w);
47
+ r.force ? a.set(p, n) : a.create(p, n);
46
48
  }
47
49
  try {
48
50
  await a.commit();
@@ -1 +1 @@
1
- {"version":3,"file":"collection.js","sources":["../../../../src/cli/utils/collection.ts"],"sourcesContent":["import { confirm } from \"@inquirer/prompts\";\nimport { fatal, info, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport type { Firestore, FirestoreDataConverter } from \"firebase-admin/firestore\";\nimport { partition, upperFirst } from \"lodash-es\";\nimport pc from \"picocolors\";\n\ntype ImportOptions = {\n delete?: true;\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importCollection<T extends { id: string }>(\n db: Firestore,\n collection: string,\n data: T[],\n converter: FirestoreDataConverter<T>,\n options: ImportOptions,\n) {\n const newIds = new Set<string>();\n for (const doc of data) {\n if (newIds.has(doc.id)) {\n fatal(`Cannot import multiple ${collection} with the same id: ${doc.id}`);\n }\n newIds.add(doc.id);\n }\n\n if (options.delete) {\n const confirmed = await confirm({\n message: `You are about to delete all ${pc.bold(\n collection,\n )}. Any previous data will be lost, this operation cannot be undone. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n\n const ref = db.collection(collection);\n await db.recursiveDelete(ref);\n info(`${upperFirst(collection)} deleted!`);\n }\n\n const existingIds = new Set<string>();\n for (const doc of await db.collection(collection).listDocuments()) {\n existingIds.add(doc.id);\n }\n const [existing, nonExisting] = partition(data, (doc) => existingIds.has(doc.id));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Documents already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const docsToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${pc.bold(\n collection,\n )}. ${existing.length} document will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n bar.start(docsToImport.length, 0);\n\n for (let i = 0; i < docsToImport.length; i += 400) {\n bar.update(i);\n const batch = db.batch();\n for (const record of docsToImport.slice(i, i + 400)) {\n const ref = db.doc(`${collection}/${record.id}`).withConverter(converter);\n if (options.force) {\n batch.set(ref, record);\n } else {\n batch.create(ref, record);\n }\n }\n try {\n await batch.commit();\n } catch (err) {\n fatal(`Cannot import ${collection}: ${err}`);\n }\n }\n\n bar.update(docsToImport.length);\n bar.stop();\n success(`${docsToImport.length} ${collection} imported!`);\n}\n"],"names":["importCollection","db","collection","data","converter","options","newIds","doc","fatal","confirm","pc","ref","info","upperFirst","existingIds","existing","nonExisting","partition","docsToImport","bar","SingleBar","i","batch","record","err","success"],"mappings":";;;;;AAaA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,wBAAa,IAAA;AACnB,aAAWC,KAAOJ;AAChB,IAAIG,EAAO,IAAIC,EAAI,EAAE,KACnBC,EAAM,0BAA0BN,CAAU,sBAAsBK,EAAI,EAAE,EAAE,GAE1ED,EAAO,IAAIC,EAAI,EAAE;AAGnB,MAAIF,EAAQ,QAAQ;AAOlB,IANkB,MAAMI,EAAQ;AAAA,MAC9B,SAAS,+BAA+BC,EAAG;AAAA,QACzCR;AAAA,MAAA,CACD;AAAA,MACD,SAAS;AAAA,IAAA,CACV,KAECM,EAAM,kBAAkB;AAG1B,UAAMG,IAAMV,EAAG,WAAWC,CAAU;AACpC,UAAMD,EAAG,gBAAgBU,CAAG,GAC5BC,EAAK,GAAGC,EAAWX,CAAU,CAAC,WAAW;AAAA,EAC3C;AAEA,QAAMY,wBAAkB,IAAA;AACxB,aAAWP,KAAO,MAAMN,EAAG,WAAWC,CAAU,EAAE;AAChD,IAAAY,EAAY,IAAIP,EAAI,EAAE;AAExB,QAAM,CAACQ,GAAUC,CAAW,IAAIC,EAAUd,GAAM,CAACI,MAAQO,EAAY,IAAIP,EAAI,EAAE,CAAC;AAChF,EAAIQ,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DG;AAAA,IACE,gCAAgCN,CAAU;AAAA,EAAA;AAI9C,QAAMgB,IAAeb,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEtF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMN,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC,EAAG;AAAA,MACzCR;AAAA,IAAA,CACD,KAAKa,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECP,EAAM,kBAAkB;AAI5B,QAAMW,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB;AACD,EAAAD,EAAI,MAAMD,EAAa,QAAQ,CAAC;AAEhC,WAASG,IAAI,GAAGA,IAAIH,EAAa,QAAQG,KAAK,KAAK;AACjD,IAAAF,EAAI,OAAOE,CAAC;AACZ,UAAMC,IAAQrB,EAAG,MAAA;AACjB,eAAWsB,KAAUL,EAAa,MAAMG,GAAGA,IAAI,GAAG,GAAG;AACnD,YAAMV,IAAMV,EAAG,IAAI,GAAGC,CAAU,IAAIqB,EAAO,EAAE,EAAE,EAAE,cAAcnB,CAAS;AACxE,MAAIC,EAAQ,QACViB,EAAM,IAAIX,GAAKY,CAAM,IAErBD,EAAM,OAAOX,GAAKY,CAAM;AAAA,IAE5B;AACA,QAAI;AACF,YAAMD,EAAM,OAAA;AAAA,IACd,SAASE,GAAK;AACZ,MAAAhB,EAAM,iBAAiBN,CAAU,KAAKsB,CAAG,EAAE;AAAA,IAC7C;AAAA,EACF;AAEA,EAAAL,EAAI,OAAOD,EAAa,MAAM,GAC9BC,EAAI,KAAA,GACJM,EAAQ,GAAGP,EAAa,MAAM,IAAIhB,CAAU,YAAY;AAC1D;"}
1
+ {"version":3,"file":"collection.js","sources":["../../../../src/cli/utils/collection.ts"],"sourcesContent":["import { styleText } from \"node:util\";\n\nimport { confirm } from \"@inquirer/prompts\";\nimport { fatal, info, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport type { Firestore, FirestoreDataConverter } from \"firebase-admin/firestore\";\nimport { partition, upperFirst } from \"lodash-es\";\n\ntype ImportOptions = {\n delete?: true;\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importCollection<T extends { id: string }>(\n db: Firestore,\n collection: string,\n data: T[],\n converter: FirestoreDataConverter<T>,\n options: ImportOptions,\n) {\n const newIds = new Set<string>();\n for (const doc of data) {\n if (newIds.has(doc.id)) {\n fatal(`Cannot import multiple ${collection} with the same id: ${doc.id}`);\n }\n newIds.add(doc.id);\n }\n\n if (options.delete) {\n const confirmed = await confirm({\n message: `You are about to delete all ${styleText(\n \"bold\",\n collection,\n )}. Any previous data will be lost, this operation cannot be undone. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n\n const ref = db.collection(collection);\n await db.recursiveDelete(ref);\n info(`${upperFirst(collection)} deleted!`);\n }\n\n const existingIds = new Set<string>();\n for (const doc of await db.collection(collection).listDocuments()) {\n existingIds.add(doc.id);\n }\n const [existing, nonExisting] = partition(data, (doc) => existingIds.has(doc.id));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Documents already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const docsToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${styleText(\n \"bold\",\n collection,\n )}. ${existing.length} document will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n bar.start(docsToImport.length, 0);\n\n for (let i = 0; i < docsToImport.length; i += 400) {\n bar.update(i);\n const batch = db.batch();\n for (const record of docsToImport.slice(i, i + 400)) {\n const ref = db.doc(`${collection}/${record.id}`).withConverter(converter);\n if (options.force) {\n batch.set(ref, record);\n } else {\n batch.create(ref, record);\n }\n }\n try {\n await batch.commit();\n } catch (err) {\n fatal(`Cannot import ${collection}: ${err}`);\n }\n }\n\n bar.update(docsToImport.length);\n bar.stop();\n success(`${docsToImport.length} ${collection} imported!`);\n}\n"],"names":["importCollection","db","collection","data","converter","options","newIds","doc","fatal","confirm","styleText","ref","info","upperFirst","existingIds","existing","nonExisting","partition","docsToImport","bar","SingleBar","i","batch","record","err","success"],"mappings":";;;;;AAcA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,wBAAa,IAAA;AACnB,aAAWC,KAAOJ;AAChB,IAAIG,EAAO,IAAIC,EAAI,EAAE,KACnBC,EAAM,0BAA0BN,CAAU,sBAAsBK,EAAI,EAAE,EAAE,GAE1ED,EAAO,IAAIC,EAAI,EAAE;AAGnB,MAAIF,EAAQ,QAAQ;AAQlB,IAPkB,MAAMI,EAAQ;AAAA,MAC9B,SAAS,+BAA+BC;AAAA,QACtC;AAAA,QACAR;AAAA,MAAA,CACD;AAAA,MACD,SAAS;AAAA,IAAA,CACV,KAECM,EAAM,kBAAkB;AAG1B,UAAMG,IAAMV,EAAG,WAAWC,CAAU;AACpC,UAAMD,EAAG,gBAAgBU,CAAG,GAC5BC,EAAK,GAAGC,EAAWX,CAAU,CAAC,WAAW;AAAA,EAC3C;AAEA,QAAMY,wBAAkB,IAAA;AACxB,aAAWP,KAAO,MAAMN,EAAG,WAAWC,CAAU,EAAE;AAChD,IAAAY,EAAY,IAAIP,EAAI,EAAE;AAExB,QAAM,CAACQ,GAAUC,CAAW,IAAIC,EAAUd,GAAM,CAACI,MAAQO,EAAY,IAAIP,EAAI,EAAE,CAAC;AAChF,EAAIQ,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DG;AAAA,IACE,gCAAgCN,CAAU;AAAA,EAAA;AAI9C,QAAMgB,IAAeb,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEtF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMN,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC;AAAA,MACtC;AAAA,MACAR;AAAA,IAAA,CACD,KAAKa,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECP,EAAM,kBAAkB;AAI5B,QAAMW,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB;AACD,EAAAD,EAAI,MAAMD,EAAa,QAAQ,CAAC;AAEhC,WAASG,IAAI,GAAGA,IAAIH,EAAa,QAAQG,KAAK,KAAK;AACjD,IAAAF,EAAI,OAAOE,CAAC;AACZ,UAAMC,IAAQrB,EAAG,MAAA;AACjB,eAAWsB,KAAUL,EAAa,MAAMG,GAAGA,IAAI,GAAG,GAAG;AACnD,YAAMV,IAAMV,EAAG,IAAI,GAAGC,CAAU,IAAIqB,EAAO,EAAE,EAAE,EAAE,cAAcnB,CAAS;AACxE,MAAIC,EAAQ,QACViB,EAAM,IAAIX,GAAKY,CAAM,IAErBD,EAAM,OAAOX,GAAKY,CAAM;AAAA,IAE5B;AACA,QAAI;AACF,YAAMD,EAAM,OAAA;AAAA,IACd,SAASE,GAAK;AACZ,MAAAhB,EAAM,iBAAiBN,CAAU,KAAKsB,CAAG,EAAE;AAAA,IAC7C;AAAA,EACF;AAEA,EAAAL,EAAI,OAAOD,EAAa,MAAM,GAC9BC,EAAI,KAAA,GACJM,EAAQ,GAAGP,EAAa,MAAM,IAAIhB,CAAU,YAAY;AAC1D;"}
@@ -1,43 +1,42 @@
1
- import { contestSchema as a, participationSchema as p, studentSchema as u, variantSchema as c } from "@olinfo/quizms/models";
2
- import { validate as d } from "@olinfo/quizms/utils";
3
- import { Timestamp as e } from "firebase-admin/firestore";
4
- import { cloneDeepWith as s, omit as m, isDate as v, isString as D, mapValues as S } from "lodash-es";
5
- import o, { ZodDate as Z, ZodObject as l, ZodRecord as C, ZodArray as b, ZodOptional as F, ZodDefault as w, ZodUnion as U, ZodDiscriminatedUnion as j } from "zod";
6
- import { userSchema as y } from "../../models/user.js";
7
- import { websiteSchema as O } from "../../models/website.js";
8
- function T(t) {
9
- return s(m(t, "id"), (r) => {
10
- if (v(r))
11
- return e.fromDate(r);
12
- if (D(r))
13
- return r.trim();
14
- if (r === void 0)
1
+ import { contestSchema as c, participationSchema as m, studentSchema as s, variantSchema as a } from "@olinfo/quizms/models";
2
+ import { validate as f } from "@olinfo/quizms/utils";
3
+ import { Timestamp as o } from "firebase-admin/firestore";
4
+ import { cloneDeepWith as n, omit as p, isDate as u, isString as d } from "lodash-es";
5
+ import { userSchema as v } from "../../models/user.js";
6
+ import { websiteSchema as S } from "../../models/website.js";
7
+ function h(r) {
8
+ return n(p(r, "id"), (t) => {
9
+ if (u(t))
10
+ return o.fromDate(t);
11
+ if (d(t))
12
+ return t.trim();
13
+ if (t === void 0)
15
14
  return null;
16
15
  });
17
16
  }
18
- function n(t) {
19
- return t instanceof Z ? o.instanceof(e).transform((r) => r.toDate()).pipe(t) : t instanceof l ? o.object(S(t.shape, (r) => n(r))) : t instanceof C ? o.record(n(t.element)) : t instanceof b ? n(t.element).array() : t instanceof F ? o.preprocess((r) => r ?? void 0, n(t.unwrap()).optional()) : t instanceof w ? n(t.removeDefault()).pipe(t) : t instanceof U ? o.union(t.options.map((r) => n(r))) : t instanceof j ? o.discriminatedUnion(
20
- t.discriminator,
21
- t.options.map((r) => n(r))
22
- ) : t;
17
+ function C(r) {
18
+ return n(r, (t) => {
19
+ if (t instanceof o)
20
+ return t.toDate();
21
+ });
23
22
  }
24
- function g(t, r) {
25
- const f = { ...r.data(), id: r.id };
26
- return d(n(t), f);
23
+ function F(r, t) {
24
+ const i = C({ ...t.data(), id: t.id });
25
+ return f(r, i);
27
26
  }
28
- function i(t) {
27
+ function e(r) {
29
28
  return {
30
- toFirestore: (r) => T(r),
31
- fromFirestore: (r) => g(t, r)
29
+ toFirestore: (t) => h(t),
30
+ fromFirestore: (t) => F(r, t)
32
31
  };
33
32
  }
34
- const q = i(a), B = i(p), E = i(u), G = i(y), H = i(c), I = i(O);
33
+ const W = e(c), j = e(m), k = e(s), q = e(v), y = e(a), z = e(S);
35
34
  export {
36
- q as contestConverter,
37
- B as participationConverter,
38
- E as studentConverter,
39
- G as userConverter,
40
- H as variantConverter,
41
- I as websiteConverter
35
+ W as contestConverter,
36
+ j as participationConverter,
37
+ k as studentConverter,
38
+ q as userConverter,
39
+ y as variantConverter,
40
+ z as websiteConverter
42
41
  };
43
42
  //# sourceMappingURL=converters-admin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"converters-admin.js","sources":["../../../../src/cli/utils/converters-admin.ts"],"sourcesContent":["import {\n contestSchema,\n participationSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n Timestamp,\n} from \"firebase-admin/firestore\";\nimport { cloneDeepWith, isDate, isString, mapValues, omit } from \"lodash-es\";\nimport z, {\n ZodArray,\n ZodDate,\n ZodDefault,\n ZodDiscriminatedUnion,\n ZodObject,\n ZodOptional,\n ZodRecord,\n type ZodType,\n type ZodTypeAny,\n ZodUnion,\n} from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: Record<string, any>) {\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 toFirebaseSchema(schema: ZodTypeAny): ZodTypeAny {\n if (schema instanceof ZodDate) {\n return z\n .instanceof(Timestamp)\n .transform((ts) => ts.toDate())\n .pipe(schema);\n }\n if (schema instanceof ZodObject) {\n return z.object(mapValues(schema.shape, (field) => toFirebaseSchema(field)));\n }\n if (schema instanceof ZodRecord) {\n return z.record(toFirebaseSchema(schema.element));\n }\n if (schema instanceof ZodArray) {\n return toFirebaseSchema(schema.element).array();\n }\n if (schema instanceof ZodOptional) {\n return z.preprocess((val) => val ?? undefined, toFirebaseSchema(schema.unwrap()).optional());\n }\n if (schema instanceof ZodDefault) {\n return toFirebaseSchema(schema.removeDefault()).pipe(schema);\n }\n if (schema instanceof ZodUnion) {\n return z.union(schema.options.map((option: ZodTypeAny) => toFirebaseSchema(option)));\n }\n if (schema instanceof ZodDiscriminatedUnion) {\n return z.discriminatedUnion(\n schema.discriminator,\n schema.options.map((option: ZodTypeAny) => toFirebaseSchema(option)),\n );\n }\n return schema;\n}\n\nfunction parse<T>(schema: ZodType<T, any, any>, snapshot: DocumentSnapshot): T {\n const data = { ...snapshot.data(), id: snapshot.id };\n return validate(toFirebaseSchema(schema), data);\n}\n\nfunction converter<T extends object>(schema: ZodType<T, any, any>): 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 studentConverter = converter(studentSchema);\nexport const userConverter = converter(userSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","toFirebaseSchema","schema","ZodDate","z","ts","ZodObject","mapValues","field","ZodRecord","ZodArray","ZodOptional","val","ZodDefault","ZodUnion","option","ZodDiscriminatedUnion","parse","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentConverter","studentSchema","userConverter","userSchema","variantConverter","variantSchema","websiteConverter","websiteSchema"],"mappings":";;;;;;;AA6BA,SAASA,EAAmBC,GAA2B;AACrD,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,EAAiBC,GAAgC;AACxD,SAAIA,aAAkBC,IACbC,EACJ,WAAWL,CAAS,EACpB,UAAU,CAACM,MAAOA,EAAG,OAAA,CAAQ,EAC7B,KAAKH,CAAM,IAEZA,aAAkBI,IACbF,EAAE,OAAOG,EAAUL,EAAO,OAAO,CAACM,MAAUP,EAAiBO,CAAK,CAAC,CAAC,IAEzEN,aAAkBO,IACbL,EAAE,OAAOH,EAAiBC,EAAO,OAAO,CAAC,IAE9CA,aAAkBQ,IACbT,EAAiBC,EAAO,OAAO,EAAE,MAAA,IAEtCA,aAAkBS,IACbP,EAAE,WAAW,CAACQ,MAAQA,KAAO,QAAWX,EAAiBC,EAAO,QAAQ,EAAE,SAAA,CAAU,IAEzFA,aAAkBW,IACbZ,EAAiBC,EAAO,cAAA,CAAe,EAAE,KAAKA,CAAM,IAEzDA,aAAkBY,IACbV,EAAE,MAAMF,EAAO,QAAQ,IAAI,CAACa,MAAuBd,EAAiBc,CAAM,CAAC,CAAC,IAEjFb,aAAkBc,IACbZ,EAAE;AAAA,IACPF,EAAO;AAAA,IACPA,EAAO,QAAQ,IAAI,CAACa,MAAuBd,EAAiBc,CAAM,CAAC;AAAA,EAAA,IAGhEb;AACT;AAEA,SAASe,EAASf,GAA8BgB,GAA+B;AAC7E,QAAMxB,IAAO,EAAE,GAAGwB,EAAS,QAAQ,IAAIA,EAAS,GAAA;AAChD,SAAOC,EAASlB,EAAiBC,CAAM,GAAGR,CAAI;AAChD;AAEA,SAAS0B,EAA4BlB,GAAyD;AAC5F,SAAO;AAAA,IACL,aAAa,CAACR,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACwB,MAAaD,EAAMf,GAAQgB,CAAQ;AAAA,EAAA;AAEvD;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAyBH,EAAUI,CAAmB,GACtDC,IAAmBL,EAAUM,CAAa,GAC1CC,IAAgBP,EAAUQ,CAAU,GACpCC,IAAmBT,EAAUU,CAAa,GAC1CC,IAAmBX,EAAUY,CAAa;"}
1
+ {"version":3,"file":"converters-admin.js","sources":["../../../../src/cli/utils/converters-admin.ts"],"sourcesContent":["import {\n contestSchema,\n participationSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n Timestamp,\n} from \"firebase-admin/firestore\";\nimport { cloneDeepWith, isDate, isString, omit } from \"lodash-es\";\nimport type z from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\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 studentConverter = converter(studentSchema);\nexport const userConverter = converter(userSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","convertFromFirestore","parse","schema","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentConverter","studentSchema","userConverter","userSchema","variantConverter","variantSchema","websiteConverter","websiteSchema"],"mappings":";;;;;;AAkBA,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,IAAmBL,EAAUM,CAAa,GAC1CC,IAAgBP,EAAUQ,CAAU,GACpCC,IAAmBT,EAAUU,CAAa,GAC1CC,IAAmBX,EAAUY,CAAa;"}
@@ -1,4 +1,4 @@
1
- import { validate as g } from "@olinfo/quizms/utils";
1
+ import { validate as p } from "@olinfo/quizms/utils";
2
2
  import { intersection as m, sortBy as h } from "lodash-es";
3
3
  import t from "zod";
4
4
  const f = [
@@ -25,21 +25,22 @@ async function q() {
25
25
  async function d(n, a) {
26
26
  const e = await fetch(n);
27
27
  if (!e.ok) throw new Error("Request failed");
28
- return g(a, await e.json());
28
+ return p(a, await e.json());
29
29
  }
30
30
  const b = t.record(
31
+ t.string(),
31
32
  t.object({
32
33
  name: t.string(),
33
34
  latitude: t.number(),
34
35
  longitude: t.number()
35
36
  })
36
- ), R = t.record(t.record(t.boolean())), M = t.object({
37
+ ), R = t.record(t.string(), t.record(t.string(), t.boolean())), M = t.object({
37
38
  latitude: t.number(),
38
39
  longitude: t.number()
39
40
  }), w = 6371e3, r = Math.PI / 180;
40
41
  function j(n, a) {
41
- const e = n.latitude * r, i = n.longitude * r, o = a.latitude * r, s = a.longitude * r, c = o - e, u = s - i, l = Math.sin(c / 2) ** 2 + Math.cos(e) * Math.cos(o) * Math.sin(u / 2) ** 2, p = 2 * Math.atan2(Math.sqrt(l), Math.sqrt(1 - l));
42
- return w * p;
42
+ const e = n.latitude * r, i = n.longitude * r, o = a.latitude * r, s = a.longitude * r, c = o - e, u = s - i, l = Math.sin(c / 2) ** 2 + Math.cos(e) * Math.cos(o) * Math.sin(u / 2) ** 2, g = 2 * Math.atan2(Math.sqrt(l), Math.sqrt(1 - l));
43
+ return w * g;
43
44
  }
44
45
  export {
45
46
  q as getGcpRegions,
@@ -1 +1 @@
1
- {"version":3,"file":"region-finder.js","sources":["../../../../src/cli/utils/region-finder.ts"],"sourcesContent":["import { validate } from \"@olinfo/quizms/utils\";\nimport { intersection, sortBy } from \"lodash-es\";\nimport z, { type ZodType } from \"zod\";\n\nconst productEnabled = [\n \"Cloud Firestore\",\n \"Cloud Run functions\",\n \"Cloud Storage for Firebase\",\n \"Cloud Tasks\",\n];\n\nexport async function getGcpRegions() {\n const [regions, products, geoip] = await Promise.all([\n api(\"https://cloud.withgoogle.com/region-picker/data/regions.json\", regionsSchema),\n api(\"https://cloud.withgoogle.com/region-picker/data/products.json\", productsSchema),\n api(\"https://api.seeip.org/geoip\", geoipSchema),\n ]);\n\n const availableRegions = intersection(\n ...productEnabled.map((p) =>\n Object.entries(products[p])\n .filter(([_, available]) => available)\n .map(([region]) => region),\n ),\n );\n\n return sortBy(\n availableRegions.map((region) => ({ region, ...regions[region] })),\n (region) => haversineDistance(geoip, region),\n );\n}\n\nasync function api<T>(url: string, schema: ZodType<T, any, any>) {\n const resp = await fetch(url);\n if (!resp.ok) throw new Error(\"Request failed\");\n return validate(schema, await resp.json());\n}\n\nconst regionsSchema = z.record(\n z.object({\n name: z.string(),\n latitude: z.number(),\n longitude: z.number(),\n }),\n);\n\nconst productsSchema = z.record(z.record(z.boolean()));\n\nconst geoipSchema = z.object({\n latitude: z.number(),\n longitude: z.number(),\n});\n\nconst R = 6371e3;\nconst toRad = Math.PI / 180;\n\nexport function haversineDistance(\n coord1: { latitude: number; longitude: number },\n coord2: { latitude: number; longitude: number },\n): number {\n const lat1Rad = coord1.latitude * toRad;\n const lng1Rad = coord1.longitude * toRad;\n const lat2Rad = coord2.latitude * toRad;\n const lng2Rad = coord2.longitude * toRad;\n\n const deltaLat = lat2Rad - lat1Rad;\n const deltaLon = lng2Rad - lng1Rad;\n\n const a =\n Math.sin(deltaLat / 2) ** 2 +\n Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(deltaLon / 2) ** 2;\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return R * c;\n}\n"],"names":["productEnabled","getGcpRegions","regions","products","geoip","api","regionsSchema","productsSchema","geoipSchema","availableRegions","intersection","p","_","available","region","sortBy","haversineDistance","url","schema","resp","validate","z","R","toRad","coord1","coord2","lat1Rad","lng1Rad","lat2Rad","lng2Rad","deltaLat","deltaLon","a","c"],"mappings":";;;AAIA,MAAMA,IAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,eAAsBC,IAAgB;AACpC,QAAM,CAACC,GAASC,GAAUC,CAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACnDC,EAAI,gEAAgEC,CAAa;AAAA,IACjFD,EAAI,iEAAiEE,CAAc;AAAA,IACnFF,EAAI,+BAA+BG,CAAW;AAAA,EAAA,CAC/C,GAEKC,IAAmBC;AAAA,IACvB,GAAGV,EAAe;AAAA,MAAI,CAACW,MACrB,OAAO,QAAQR,EAASQ,CAAC,CAAC,EACvB,OAAO,CAAC,CAACC,GAAGC,CAAS,MAAMA,CAAS,EACpC,IAAI,CAAC,CAACC,CAAM,MAAMA,CAAM;AAAA,IAAA;AAAA,EAC7B;AAGF,SAAOC;AAAA,IACLN,EAAiB,IAAI,CAACK,OAAY,EAAE,QAAAA,GAAQ,GAAGZ,EAAQY,CAAM,EAAA,EAAI;AAAA,IACjE,CAACA,MAAWE,EAAkBZ,GAAOU,CAAM;AAAA,EAAA;AAE/C;AAEA,eAAeT,EAAOY,GAAaC,GAA8B;AAC/D,QAAMC,IAAO,MAAM,MAAMF,CAAG;AAC5B,MAAI,CAACE,EAAK,GAAI,OAAM,IAAI,MAAM,gBAAgB;AAC9C,SAAOC,EAASF,GAAQ,MAAMC,EAAK,MAAM;AAC3C;AAEA,MAAMb,IAAgBe,EAAE;AAAA,EACtBA,EAAE,OAAO;AAAA,IACP,MAAMA,EAAE,OAAA;AAAA,IACR,UAAUA,EAAE,OAAA;AAAA,IACZ,WAAWA,EAAE,OAAA;AAAA,EAAO,CACrB;AACH,GAEMd,IAAiBc,EAAE,OAAOA,EAAE,OAAOA,EAAE,QAAA,CAAS,CAAC,GAE/Cb,IAAca,EAAE,OAAO;AAAA,EAC3B,UAAUA,EAAE,OAAA;AAAA,EACZ,WAAWA,EAAE,OAAA;AACf,CAAC,GAEKC,IAAI,QACJC,IAAQ,KAAK,KAAK;AAEjB,SAASP,EACdQ,GACAC,GACQ;AACR,QAAMC,IAAUF,EAAO,WAAWD,GAC5BI,IAAUH,EAAO,YAAYD,GAC7BK,IAAUH,EAAO,WAAWF,GAC5BM,IAAUJ,EAAO,YAAYF,GAE7BO,IAAWF,IAAUF,GACrBK,IAAWF,IAAUF,GAErBK,IACJ,KAAK,IAAIF,IAAW,CAAC,KAAK,IAC1B,KAAK,IAAIJ,CAAO,IAAI,KAAK,IAAIE,CAAO,IAAI,KAAK,IAAIG,IAAW,CAAC,KAAK,GAC9DE,IAAI,IAAI,KAAK,MAAM,KAAK,KAAKD,CAAC,GAAG,KAAK,KAAK,IAAIA,CAAC,CAAC;AAEvD,SAAOV,IAAIW;AACb;"}
1
+ {"version":3,"file":"region-finder.js","sources":["../../../../src/cli/utils/region-finder.ts"],"sourcesContent":["import { validate } from \"@olinfo/quizms/utils\";\nimport { intersection, sortBy } from \"lodash-es\";\nimport z from \"zod\";\n\nconst productEnabled = [\n \"Cloud Firestore\",\n \"Cloud Run functions\",\n \"Cloud Storage for Firebase\",\n \"Cloud Tasks\",\n];\n\nexport async function getGcpRegions() {\n const [regions, products, geoip] = await Promise.all([\n api(\"https://cloud.withgoogle.com/region-picker/data/regions.json\", regionsSchema),\n api(\"https://cloud.withgoogle.com/region-picker/data/products.json\", productsSchema),\n api(\"https://api.seeip.org/geoip\", geoipSchema),\n ]);\n\n const availableRegions = intersection(\n ...productEnabled.map((p) =>\n Object.entries(products[p])\n .filter(([_, available]) => available)\n .map(([region]) => region),\n ),\n );\n\n return sortBy(\n availableRegions.map((region) => ({ region, ...regions[region] })),\n (region) => haversineDistance(geoip, region),\n );\n}\n\nasync function api<T>(url: string, schema: z.core.$ZodType<T>) {\n const resp = await fetch(url);\n if (!resp.ok) throw new Error(\"Request failed\");\n return validate(schema, await resp.json());\n}\n\nconst regionsSchema = z.record(\n z.string(),\n z.object({\n name: z.string(),\n latitude: z.number(),\n longitude: z.number(),\n }),\n);\n\nconst productsSchema = z.record(z.string(), z.record(z.string(), z.boolean()));\n\nconst geoipSchema = z.object({\n latitude: z.number(),\n longitude: z.number(),\n});\n\nconst R = 6371e3;\nconst toRad = Math.PI / 180;\n\nexport function haversineDistance(\n coord1: { latitude: number; longitude: number },\n coord2: { latitude: number; longitude: number },\n): number {\n const lat1Rad = coord1.latitude * toRad;\n const lng1Rad = coord1.longitude * toRad;\n const lat2Rad = coord2.latitude * toRad;\n const lng2Rad = coord2.longitude * toRad;\n\n const deltaLat = lat2Rad - lat1Rad;\n const deltaLon = lng2Rad - lng1Rad;\n\n const a =\n Math.sin(deltaLat / 2) ** 2 +\n Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(deltaLon / 2) ** 2;\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return R * c;\n}\n"],"names":["productEnabled","getGcpRegions","regions","products","geoip","api","regionsSchema","productsSchema","geoipSchema","availableRegions","intersection","p","_","available","region","sortBy","haversineDistance","url","schema","resp","validate","z","R","toRad","coord1","coord2","lat1Rad","lng1Rad","lat2Rad","lng2Rad","deltaLat","deltaLon","a","c"],"mappings":";;;AAIA,MAAMA,IAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,eAAsBC,IAAgB;AACpC,QAAM,CAACC,GAASC,GAAUC,CAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACnDC,EAAI,gEAAgEC,CAAa;AAAA,IACjFD,EAAI,iEAAiEE,CAAc;AAAA,IACnFF,EAAI,+BAA+BG,CAAW;AAAA,EAAA,CAC/C,GAEKC,IAAmBC;AAAA,IACvB,GAAGV,EAAe;AAAA,MAAI,CAACW,MACrB,OAAO,QAAQR,EAASQ,CAAC,CAAC,EACvB,OAAO,CAAC,CAACC,GAAGC,CAAS,MAAMA,CAAS,EACpC,IAAI,CAAC,CAACC,CAAM,MAAMA,CAAM;AAAA,IAAA;AAAA,EAC7B;AAGF,SAAOC;AAAA,IACLN,EAAiB,IAAI,CAACK,OAAY,EAAE,QAAAA,GAAQ,GAAGZ,EAAQY,CAAM,EAAA,EAAI;AAAA,IACjE,CAACA,MAAWE,EAAkBZ,GAAOU,CAAM;AAAA,EAAA;AAE/C;AAEA,eAAeT,EAAOY,GAAaC,GAA4B;AAC7D,QAAMC,IAAO,MAAM,MAAMF,CAAG;AAC5B,MAAI,CAACE,EAAK,GAAI,OAAM,IAAI,MAAM,gBAAgB;AAC9C,SAAOC,EAASF,GAAQ,MAAMC,EAAK,MAAM;AAC3C;AAEA,MAAMb,IAAgBe,EAAE;AAAA,EACtBA,EAAE,OAAA;AAAA,EACFA,EAAE,OAAO;AAAA,IACP,MAAMA,EAAE,OAAA;AAAA,IACR,UAAUA,EAAE,OAAA;AAAA,IACZ,WAAWA,EAAE,OAAA;AAAA,EAAO,CACrB;AACH,GAEMd,IAAiBc,EAAE,OAAOA,EAAE,UAAUA,EAAE,OAAOA,EAAE,OAAA,GAAUA,EAAE,QAAA,CAAS,CAAC,GAEvEb,IAAca,EAAE,OAAO;AAAA,EAC3B,UAAUA,EAAE,OAAA;AAAA,EACZ,WAAWA,EAAE,OAAA;AACf,CAAC,GAEKC,IAAI,QACJC,IAAQ,KAAK,KAAK;AAEjB,SAASP,EACdQ,GACAC,GACQ;AACR,QAAMC,IAAUF,EAAO,WAAWD,GAC5BI,IAAUH,EAAO,YAAYD,GAC7BK,IAAUH,EAAO,WAAWF,GAC5BM,IAAUJ,EAAO,YAAYF,GAE7BO,IAAWF,IAAUF,GACrBK,IAAWF,IAAUF,GAErBK,IACJ,KAAK,IAAIF,IAAW,CAAC,KAAK,IAC1B,KAAK,IAAIJ,CAAO,IAAI,KAAK,IAAIE,CAAO,IAAI,KAAK,IAAIG,IAAW,CAAC,KAAK,GAC9DE,IAAI,IAAI,KAAK,MAAM,KAAK,KAAKD,CAAC,GAAG,KAAK,KAAK,IAAIA,CAAC,CAAC;AAEvD,SAAOV,IAAIW;AACb;"}
@@ -1,44 +1,35 @@
1
- import { existsSync as h } from "node:fs";
1
+ import { existsSync as c } from "node:fs";
2
+ import { styleText as h } from "node:util";
2
3
  import { confirm as u } from "@inquirer/prompts";
3
- import { AsyncPool as w } from "@olinfo/quizms/utils";
4
- import { fatal as m, success as x } from "@olinfo/quizms/utils-node";
5
- import { SingleBar as b } from "cli-progress";
6
- import { partition as y } from "lodash-es";
7
- import $ from "picocolors";
8
- async function I(f, i, s, r) {
9
- const t = new b({
10
- format: " {bar} {percentage}% | {value}/{total}",
11
- barCompleteChar: "█",
12
- barIncompleteChar: "▂"
13
- }), l = /* @__PURE__ */ new Set();
14
- t.start(s.length, 0), await Promise.all(
15
- s.map(async ([o, e]) => {
16
- h(o) || m(
17
- `Cannot find ${i}. Make sure to generate them first, use \`quizms --help\` for usage.`
18
- );
19
- const g = f.file(e), [d] = await g.exists().catch(() => [!1]);
20
- d && l.add(e), t.increment();
21
- })
22
- ), t.stop();
23
- const [a, c] = y(s, ([o, e]) => l.has(e));
24
- a.length > 0 && !r.skipExisting && !r.force && m(
4
+ import { withProgress as g, fatal as n, success as x } from "@olinfo/quizms/utils-node";
5
+ import { partition as w } from "lodash-es";
6
+ async function E(f, i, o, s) {
7
+ const m = /* @__PURE__ */ new Set();
8
+ await g(o, o.length, async ([e, t]) => {
9
+ c(e) || n(
10
+ `Cannot find ${i}. Make sure to generate them first, use \`quizms --help\` for usage.`
11
+ );
12
+ const d = f.file(t), [p] = await d.exists().catch(() => [!1]);
13
+ p && m.add(t);
14
+ });
15
+ const [r, l] = w(o, ([e, t]) => m.has(t));
16
+ r.length > 0 && !s.skipExisting && !s.force && n(
25
17
  `Files already exist in \`${i}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
26
18
  );
27
- const n = r.skipExisting ? c : [...a, ...c];
28
- r.force && a.length > 0 && (await u({
29
- message: `You are about to import the ${$.bold(
19
+ const a = s.skipExisting ? l : [...r, ...l];
20
+ s.force && r.length > 0 && (await u({
21
+ message: `You are about to import the ${h(
22
+ "bold",
30
23
  i
31
- )}. ${a.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
24
+ )}. ${r.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
32
25
  default: !1
33
- }) || m("Command aborted.")), t.start(n.length, 0);
34
- const p = new w(8);
35
- for (const [o, e] of n)
36
- p.run(async () => {
37
- await f.upload(o, { destination: e }), t.increment();
38
- });
39
- await p.wait(), t.stop(), x(`${n.length} ${i} imported!`);
26
+ }) || n("Command aborted.")), await g(
27
+ a,
28
+ a.length,
29
+ ([e, t]) => f.upload(e, { destination: t })
30
+ ), x(`${a.length} ${i} imported!`);
40
31
  }
41
32
  export {
42
- I as importStorage
33
+ E as importStorage
43
34
  };
44
35
  //# sourceMappingURL=storage.js.map