@examplary/ui 1.84.1 → 1.85.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.
@@ -1,4 +1,8 @@
1
1
  import { ComponentType } from "react";
2
2
  export declare const loadComponentStyles: (url: string) => void;
3
3
  export declare const evalComponent: (js: string) => Promise<ComponentType>;
4
- export declare const fetchComponent: (url: string) => Promise<ComponentType>;
4
+ /**
5
+ * Loads and evaluates the component at `url`. With a `sha256`, a component
6
+ * that doesn't match it (e.g. replaced after a review) is refused.
7
+ */
8
+ export declare const fetchComponent: (url: string, sha256?: string) => Promise<ComponentType>;
@@ -78,9 +78,15 @@ export const evalComponent = async (js) => {
78
78
  })();
79
79
  return _componentResult;
80
80
  };
81
- export const fetchComponent = async (url) => {
82
- if (componentCache.has(url)) {
83
- return componentCache.get(url);
81
+ const sha256Of = async (text) => Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))), (byte) => byte.toString(16).padStart(2, "0")).join("");
82
+ /**
83
+ * Loads and evaluates the component at `url`. With a `sha256`, a component
84
+ * that doesn't match it (e.g. replaced after a review) is refused.
85
+ */
86
+ export const fetchComponent = async (url, sha256) => {
87
+ const cacheKey = sha256 ? `${sha256}:${url}` : url;
88
+ if (componentCache.has(cacheKey)) {
89
+ return componentCache.get(cacheKey);
84
90
  }
85
91
  const js = await fetch(url).then((res) => {
86
92
  if (!res.ok) {
@@ -88,7 +94,10 @@ export const fetchComponent = async (url) => {
88
94
  }
89
95
  return res.text();
90
96
  });
97
+ if (sha256 && (await sha256Of(js)) !== sha256) {
98
+ throw new Error(`The component at ${url} doesn't match its digest`);
99
+ }
91
100
  const res = await evalComponent(js);
92
- componentCache.set(url, res);
101
+ componentCache.set(cacheKey, res);
93
102
  return res;
94
103
  };
@@ -84,7 +84,7 @@ export declare const formatQuestionType: (type: QuestionType, i18n: i18n) => {
84
84
  "settings-area"?: string | undefined;
85
85
  results?: string | undefined;
86
86
  } | undefined;
87
- functions?: Record<string, string> | undefined;
87
+ hooks?: Record<string, string> | undefined;
88
88
  public?: boolean | undefined;
89
89
  deprecated?: boolean | undefined;
90
90
  translations?: Record<string, string | Record<string, string>> | undefined;
@@ -118,9 +118,11 @@ export declare const formatQuestionType: (type: QuestionType, i18n: i18n) => {
118
118
  settings?: Record<string, string | number | boolean | any[] | Record<string, any>> | undefined;
119
119
  } | undefined;
120
120
  } | undefined;
121
- metadata?: Record<string, string | number | boolean | null> | undefined;
121
+ metadata?: Record<string, import("zod").JSONType> | undefined;
122
122
  readme?: string | undefined;
123
+ readmeUrls?: Record<string, string> | undefined;
123
124
  previewImage?: string | undefined;
125
+ previewImageUrls?: Record<string, string> | undefined;
124
126
  previewData?: string | undefined;
125
127
  stylesheet?: string | undefined;
126
128
  index?: number | undefined;
@@ -30,7 +30,6 @@ export type FrontendBaseComponentProps = {
30
30
  t: (key: string | string[], options?: any) => string;
31
31
  api: PublicApiInstance;
32
32
  i18n: any;
33
- environment: FrontendComponentEnvironment;
34
33
  };
35
34
  export type FrontendAssessmentComponent = ComponentType<FrontendBaseComponentProps & {
36
35
  question: any;
@@ -39,16 +38,19 @@ export type FrontendAssessmentComponent = ComponentType<FrontendBaseComponentPro
39
38
  reviewMode?: boolean;
40
39
  answer?: Answer;
41
40
  saveAnswer: (answer: Answer) => Promise<void>;
41
+ environment: FrontendComponentEnvironment;
42
42
  }>;
43
43
  export type FrontendPrintComponent = ComponentType<FrontendBaseComponentProps & {
44
44
  question: any;
45
45
  answerBoxes: boolean;
46
46
  api: OrgApiInstance;
47
+ environment: FrontendComponentEnvironment;
47
48
  }>;
48
49
  export type FrontendResultsComponent = ComponentType<FrontendBaseComponentProps & {
49
50
  question: any;
50
51
  answer: Answer;
51
52
  api: OrgApiInstance;
53
+ environment: FrontendComponentEnvironment;
52
54
  }>;
53
55
  export type FrontendQuestionSettingsAreaComponent = ComponentType<FrontendBaseComponentProps & {
54
56
  question: any;
@@ -56,5 +58,42 @@ export type FrontendQuestionSettingsAreaComponent = ComponentType<FrontendBaseCo
56
58
  api: OrgApiInstance;
57
59
  setSetting: (settingId: string, value: any) => void;
58
60
  setMultipleSettings: (newSettings: Record<string, any>) => void;
61
+ environment: FrontendComponentEnvironment;
62
+ }>;
63
+ export type FrontendSettingsFieldComponent = ComponentType<FrontendBaseComponentProps & {
64
+ setValue: (field: string, value: any) => void;
65
+ submit: () => Promise<any>;
66
+ }>;
67
+ type PrerequisiteComponentProps = FrontendBaseComponentProps & {
68
+ /** The prerequisite's settings for this exam */
69
+ settings: Record<string, any>;
70
+ /** The app's icon */
71
+ icon?: string;
72
+ /** Runs one of the prerequisite's functions on the server, returning its result */
73
+ callFunction: (name: string, input?: unknown) => Promise<any>;
74
+ };
75
+ /** Shown in the exam settings once an app's prerequisite is turned on. */
76
+ export type FrontendPrerequisiteSettingsComponent = ComponentType<PrerequisiteComponentProps & {
77
+ exam: {
78
+ id: string;
79
+ published: boolean;
80
+ startDateTime?: string;
81
+ endDateTime?: string;
82
+ };
83
+ }>;
84
+ /** Shown to students while an app's prerequisite isn't met. */
85
+ export type FrontendPrerequisitePlayerComponent = ComponentType<PrerequisiteComponentProps & {
86
+ exam: {
87
+ id: string;
88
+ privateUrl: string;
89
+ };
90
+ user: {
91
+ isAuthenticated: boolean;
92
+ role?: string;
93
+ };
94
+ /** Passes a value to the prerequisite's `isMet` and lets the student start */
95
+ setValue: (value: string) => void;
96
+ /** The public URL of one of the prerequisite's files */
97
+ fileUrl: (name: string) => string;
59
98
  }>;
60
99
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@examplary/ui",
3
3
  "description": "UI components for the Examplary testing platform.",
4
4
  "packageManager": "yarn@4.5.3",
5
- "version": "1.84.1",
5
+ "version": "1.85.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -44,7 +44,7 @@
44
44
  "@tiptap/core": "^3.17.1",
45
45
  "@tiptap/extension-bold": "^3.11.1",
46
46
  "@tiptap/extension-collaboration": "^3.22.2",
47
- "@tiptap/extension-document": "^3.11.1",
47
+ "@tiptap/extension-document": "^3.32.0",
48
48
  "@tiptap/extension-file-handler": "^3.11.1",
49
49
  "@tiptap/extension-highlight": "^3.19.0",
50
50
  "@tiptap/extension-image": "^3.22.5",