diff --git a/content-schema/.gitignore b/content-schema/.gitignore
new file mode 100644
index 0000000..21f0050
--- /dev/null
+++ b/content-schema/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+playgound
diff --git a/content-schema/README.md b/content-schema/README.md
new file mode 100644
index 0000000..acad907
--- /dev/null
+++ b/content-schema/README.md
@@ -0,0 +1 @@
+# Content schema
diff --git a/content-schema/example/courses/test-course-1/course.yaml b/content-schema/example/courses/test-course-1/course.yaml
new file mode 100644
index 0000000..8e7aa49
--- /dev/null
+++ b/content-schema/example/courses/test-course-1/course.yaml
@@ -0,0 +1,14 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/ArturW1998/course-app/main/content-schema/schemas/course.schema.json
+
+id: "{cuid}"
+thumbnail: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
+dependencies:
+ - "test-course-2"
+title: "Test course"
+description: |
+ ## Course the best
+ - 1
+ - 2
+ - [3](https://google.com)
+lessons:
+ - "lesson-1"
diff --git a/content-schema/example/courses/test-course-1/lessons/lesson-1/lesson.yaml b/content-schema/example/courses/test-course-1/lessons/lesson-1/lesson.yaml
new file mode 100644
index 0000000..eb800ad
--- /dev/null
+++ b/content-schema/example/courses/test-course-1/lessons/lesson-1/lesson.yaml
@@ -0,0 +1,45 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/ArturW1998/course-app/main/content-schema/schemas/lesson.schema.json
+id: "{cuid}"
+title: "Advanced Python Programming"
+blocks:
+ - type: "text"
+ id: "{cuid}"
+ text: |
+
+
+
+ ## What we're going to study today
+
+ It's an open question.
+ Some text
+
+ - type: "video"
+ id: "{cuid}"
+ url: "https://example.com/video/python-advanced"
+
+ - type: "text"
+ id: "{cuid}"
+ text: |
+ ## Introduction to Advanced Python Programming
+
+ In this section, we'll dive into advanced Python programming topics, including:
+ - OOP (Object Oriented Programming)
+ - Working with databases
+ - Multithreading and asynchronous programming
+
+ - type: "question"
+ id: "{cuid}"
+ answerOptions:
+ - text: "What design patterns are most commonly used in Python?"
+ id: "{cuid}"
+ isRight: false
+ - text: "What is a list in Python and how is it used?"
+ id: "{cuid}"
+ isRight: true
+ explanation: |
+ ### Explanation
+
+ This quiz will help consolidate your knowledge of advanced Python topics.
+ Each question is designed to test understanding of key concepts.
+ successMessage: |
+ 🎉 **Great! You have successfully completed the Advanced Python Topics quiz.**
diff --git a/content-schema/example/manifeset.yaml b/content-schema/example/manifeset.yaml
new file mode 100644
index 0000000..1ff7d53
--- /dev/null
+++ b/content-schema/example/manifeset.yaml
@@ -0,0 +1,3 @@
+# yaml-language-server: $schema=../schemas/manifest.schema.json
+courses:
+ - "test-course-1"
diff --git a/content-schema/package.json b/content-schema/package.json
new file mode 100644
index 0000000..73ceeb8
--- /dev/null
+++ b/content-schema/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@core/content-schema",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "directories": {
+ "example": "example"
+ },
+ "scripts": {
+ "gen:types": "ts-node ./scripts/generate-schema-types.ts ./schemas",
+ "replace-cuid:playgound": "ts-node ./scripts/replace-cuid.ts ./playgound",
+ "replace-cuid": "ts-node ./scripts/replace-cuid.ts",
+ "validate": "ts-node ./scripts/validate.ts",
+ "dev": "concurrently -P --kill-others \"npm:replace-cuid -- {@}\" \"npm:validate -- {@}\" --",
+ "dev:staging": "npm run dev -- ../staging-content/",
+ "dev:prod": "npm run dev -- ../prod-content/"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "concurrently": "^8.2.2",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
+ },
+ "dependencies": {
+ "ajv": "^8.12.0",
+ "chokidar": "^3.5.3",
+ "cuid": "^3.0.0",
+ "json-schema-to-typescript": "^13.1.2",
+ "yaml": "^2.3.4"
+ }
+}
diff --git a/content-schema/schemas/course.schema.d.ts b/content-schema/schemas/course.schema.d.ts
new file mode 100644
index 0000000..edd6514
--- /dev/null
+++ b/content-schema/schemas/course.schema.d.ts
@@ -0,0 +1,28 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type Cuid = string;
+export type Product =
+ | {
+ access: "free";
+ }
+ | {
+ access: "paid";
+ price: number;
+ };
+
+export interface Course {
+ id: Cuid;
+ title: string;
+ description: string;
+ shortDescription?: string;
+ thumbnail: string;
+ image: string;
+ dependencies?: Cuid[];
+ lessons: Cuid[];
+ product: Product;
+}
diff --git a/content-schema/schemas/course.schema.json b/content-schema/schemas/course.schema.json
new file mode 100644
index 0000000..3da662e
--- /dev/null
+++ b/content-schema/schemas/course.schema.json
@@ -0,0 +1,85 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": [
+ "id",
+ "title",
+ "description",
+ "lessons",
+ "thumbnail",
+ "image",
+ "product"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string"
+ },
+ "shortDescription": {
+ "type": "string"
+ },
+ "thumbnail": {
+ "type": "string",
+ "$comment": "url to image sizes 600x300"
+ },
+ "image": {
+ "type": "string",
+ "$comment": "url to image sizes 1920x600"
+ },
+ "dependencies": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/cuid"
+ }
+ },
+ "lessons": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/cuid"
+ }
+ },
+ "product": {
+ "$ref": "#/definitions/product"
+ }
+ },
+ "definitions": {
+ "cuid": {
+ "type": "string",
+ "title": "cuid"
+ },
+ "product": {
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["access"],
+ "properties": {
+ "access": {
+ "const": "free"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["access", "price"],
+ "properties": {
+ "access": {
+ "const": "paid"
+ },
+ "price": {
+ "type": "number"
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/content-schema/schemas/lesson.schema.d.ts b/content-schema/schemas/lesson.schema.d.ts
new file mode 100644
index 0000000..0c0d9a5
--- /dev/null
+++ b/content-schema/schemas/lesson.schema.d.ts
@@ -0,0 +1,38 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type Cuid = string;
+
+export interface Lesson {
+ id: Cuid;
+ title: string;
+ shortDescription?: string;
+ blocks: (TextBlock | VideoBlock | QuestionBlock)[];
+}
+export interface TextBlock {
+ id: Cuid;
+ type: "text";
+ text: string;
+}
+export interface VideoBlock {
+ id: Cuid;
+ type: "video";
+ url: string;
+}
+export interface QuestionBlock {
+ id: Cuid;
+ type: "question";
+ answerOptions: AnswerOption[];
+ explanation?: string;
+ successMessage?: string;
+ text?: string;
+}
+export interface AnswerOption {
+ id: Cuid;
+ text: string;
+ isRight: boolean;
+}
diff --git a/content-schema/schemas/lesson.schema.json b/content-schema/schemas/lesson.schema.json
new file mode 100644
index 0000000..e3a6e09
--- /dev/null
+++ b/content-schema/schemas/lesson.schema.json
@@ -0,0 +1,106 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": ["id", "title", "blocks"],
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "title": {
+ "type": "string"
+ },
+ "shortDescription": {
+ "type": "string"
+ },
+ "blocks": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "$ref": "#/block/textBlock" },
+ { "$ref": "#/block/videoBlock" },
+ { "$ref": "#/block/questionBlock" }
+ ]
+ }
+ }
+ },
+ "additionalProperties": false,
+ "block": {
+ "textBlock": {
+ "required": ["id", "type", "text"],
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "type": {
+ "const": "text"
+ },
+ "text": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "videoBlock": {
+ "required": ["id", "type", "url"],
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "type": {
+ "const": "video"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "questionBlock": {
+ "required": ["id", "type", "answerOptions"],
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "type": {
+ "const": "question"
+ },
+ "answerOptions": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/answerOption" }
+ },
+ "explanation": {
+ "type": "string"
+ },
+ "successMessage": {
+ "type": "string"
+ },
+ "text": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "definitions": {
+ "cuid": {
+ "type": "string",
+ "title": "cuid"
+ },
+ "answerOption": {
+ "type": "object",
+ "required": ["id", "text", "isRight"],
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/cuid"
+ },
+ "text": {
+ "type": "string"
+ },
+ "isRight": {
+ "type": "boolean"
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/content-schema/schemas/manifest.schema.d.ts b/content-schema/schemas/manifest.schema.d.ts
new file mode 100644
index 0000000..acd2a1c
--- /dev/null
+++ b/content-schema/schemas/manifest.schema.d.ts
@@ -0,0 +1,10 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export interface Manifest {
+ courses: string[];
+}
diff --git a/content-schema/schemas/manifest.schema.json b/content-schema/schemas/manifest.schema.json
new file mode 100644
index 0000000..50fd38f
--- /dev/null
+++ b/content-schema/schemas/manifest.schema.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "properties": {
+ "courses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": ["courses"]
+}
diff --git a/content-schema/scripts/generate-schema-types.ts b/content-schema/scripts/generate-schema-types.ts
new file mode 100644
index 0000000..5717995
--- /dev/null
+++ b/content-schema/scripts/generate-schema-types.ts
@@ -0,0 +1,41 @@
+import fs from "fs";
+import path from "path";
+import { compileFromFile } from "json-schema-to-typescript";
+import chokidar from "chokidar";
+
+async function convertSchemaToType(file: string): Promise {
+ try {
+ const ts: string = await compileFromFile(file);
+ fs.writeFileSync(
+ path.join(path.dirname(file), path.basename(file, ".json") + ".d.ts"),
+ ts
+ );
+ console.log(`Converted: ${file}`);
+ } catch (error) {
+ console.error(`Error during file conversion ${file}:`, error);
+ }
+}
+
+function watchSchemas(relativePath: string): void {
+ const directory: string = path.join(process.cwd(), relativePath);
+
+ const watcher = chokidar.watch(path.join(directory, "*.json"), {
+ ignored: /^\./,
+ persistent: true,
+ ignoreInitial: false,
+ });
+
+ watcher
+ .on("add", async (file) => {
+ console.log(`New file found: ${file}`);
+ await convertSchemaToType(file);
+ })
+ .on("change", async (file) => {
+ console.log(`Changes detected in the file: ${file}`);
+ await convertSchemaToType(file);
+ });
+}
+
+// Using a command line argument to specify a relative path
+const relativePath: string = process.argv[2];
+watchSchemas(relativePath);
diff --git a/content-schema/scripts/replace-cuid.ts b/content-schema/scripts/replace-cuid.ts
new file mode 100644
index 0000000..30bcd1e
--- /dev/null
+++ b/content-schema/scripts/replace-cuid.ts
@@ -0,0 +1,40 @@
+import fs from "fs";
+import path from "path";
+import chokidar from "chokidar";
+import cuid from "cuid";
+
+function replaceCuidInFile(file: string) {
+ try {
+ let content = fs.readFileSync(file, "utf8");
+ const updatedContent = content.replace(/{cuid}/g, () => cuid());
+
+ if (content !== updatedContent) {
+ fs.writeFileSync(file, updatedContent, "utf8");
+ console.log(`Updated: ${file}`);
+ }
+ } catch (error) {
+ console.error(`Error when updating a file ${file}:`, error);
+ }
+}
+
+function watchYAMLFiles(relativePath: string) {
+ const directory = path.join(process.cwd(), relativePath);
+ const watcher = chokidar.watch(path.join(directory, "**/*.yaml"), {
+ ignored: /^\./,
+ persistent: true,
+ ignoreInitial: false,
+ });
+
+ watcher.on("add", (file) => {
+ replaceCuidInFile(file);
+ });
+
+ // Processing when a file is changed
+ watcher.on("change", (file) => {
+ replaceCuidInFile(file);
+ });
+}
+
+// Using a command line argument to specify a relative path
+const relativePath: string = process.argv[2]; // The default is './test-structure'
+watchYAMLFiles(relativePath);
diff --git a/content-schema/scripts/validate.ts b/content-schema/scripts/validate.ts
new file mode 100644
index 0000000..d2d5c87
--- /dev/null
+++ b/content-schema/scripts/validate.ts
@@ -0,0 +1,136 @@
+import fs from "fs";
+import path from "path";
+import chokidar from "chokidar";
+import * as Yaml from "yaml";
+import Ajv from "ajv";
+import { Lesson } from "../schemas/lesson.schema";
+import { Course } from "../schemas/course.schema";
+import { Manifest } from "../schemas/manifest.schema";
+
+export class ContentParser {
+ private ajv = new Ajv({
+ strict: false,
+ });
+
+ async parse(text: string, schema: object) {
+ const resultObject: unknown = await Yaml.parse(text);
+
+ if (this.ajv.validate(schema, resultObject)) {
+ return resultObject as T;
+ } else {
+ console.log(this.ajv.errors);
+ throw this.ajv.errors;
+ }
+ }
+}
+
+const contentParser = new ContentParser();
+
+// CHECK IDS
+let ids = new Set();
+
+const checkIds = (obj: any, context: string, path = "") => {
+ if (obj && typeof obj === "object") {
+ if (obj.id) {
+ if (ids.has(obj.id)) {
+ console.error(
+ `Duplicate ID found: ${obj.id} in ${context} path: ${path}`
+ );
+ } else {
+ ids.add(obj.id);
+ }
+ }
+ Object.entries(obj).forEach(([key, value]) =>
+ checkIds(value, `${path}.${key}`, context)
+ );
+ }
+};
+
+// VALIDATE SCHEMA
+
+const lessonSchema = JSON.parse(
+ fs.readFileSync(path.resolve(__dirname, "../schemas/lesson.schema.json"), {
+ encoding: "utf8",
+ })
+);
+const courseSchema = JSON.parse(
+ fs.readFileSync(path.resolve(__dirname, "../schemas/course.schema.json"), {
+ encoding: "utf8",
+ })
+);
+const manifestSchema = JSON.parse(
+ fs.readFileSync(path.resolve(__dirname, "../schemas/manifest.schema.json"), {
+ encoding: "utf8",
+ })
+);
+
+async function validateLesson(file: string) {
+ const lesson = await contentParser.parse(
+ fs.readFileSync(file, "utf8"),
+ lessonSchema
+ );
+
+ checkIds(lesson, file);
+ return lesson;
+}
+
+async function validateCourse(file: string) {
+ const course = await contentParser.parse(
+ fs.readFileSync(file, "utf8"),
+ courseSchema
+ );
+ checkIds(course, file);
+ return course;
+}
+
+async function validateManifest(file: string) {
+ const manifest = await contentParser.parse(
+ fs.readFileSync(file, "utf8"),
+ manifestSchema
+ );
+ return manifest;
+}
+
+async function scanDirectory(directoryPath: string) {
+ ids = new Set();
+
+ const manifest = await validateManifest(`${directoryPath}/manifest.yaml`);
+
+ for (const coursePath of manifest.courses) {
+ const course = await validateCourse(
+ `${directoryPath}/courses/${coursePath}/course.yaml`
+ );
+
+ for (const lessonPath of course.lessons) {
+ await validateLesson(
+ `${directoryPath}/courses/${coursePath}/lessons/${lessonPath}/lesson.yaml`
+ );
+ }
+ }
+
+ console.log("All files are valid");
+}
+
+function watchYAMLFiles(relativePath: string) {
+ const directory = path.join(process.cwd(), relativePath);
+ const watcher = chokidar.watch(path.join(directory, "**/*.yaml"), {
+ ignored: /^\./,
+ persistent: true,
+ ignoreInitial: false,
+ });
+
+ scanDirectory(directory).catch((e) => {
+ console.error(e);
+ });
+ // Обработка при изменении файла
+ watcher.on("change", async () => {
+ try {
+ await scanDirectory(directory);
+ } catch (error) {
+ console.error(error);
+ }
+ });
+}
+
+const relativePath: string = process.argv[2];
+watchYAMLFiles(relativePath);
diff --git a/content-schema/tsconfig.json b/content-schema/tsconfig.json
new file mode 100644
index 0000000..cd6f29b
--- /dev/null
+++ b/content-schema/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+
+ /* Interop Constraints */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+
+ /* Completeness */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index e5594eb..628872b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,9 @@
"name": "core",
"version": "1.0.0",
"hasInstallScript": true,
+ "workspaces": [
+ "content-schema"
+ ],
"dependencies": {
"@auth/prisma-adapter": "^1.0.14",
"@aws-sdk/client-s3": "^3.490.0",
@@ -76,6 +79,23 @@
"node": ">=18.11.0"
}
},
+ "content-schema": {
+ "name": "@core/content-schema",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.12.0",
+ "chokidar": "^3.5.3",
+ "cuid": "^3.0.0",
+ "json-schema-to-typescript": "^13.1.2",
+ "yaml": "^2.3.4"
+ },
+ "devDependencies": {
+ "concurrently": "^8.2.2",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
+ }
+ },
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
@@ -1626,6 +1646,10 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@core/content-schema": {
+ "resolved": "content-schema",
+ "link": true
+ },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -6169,6 +6193,48 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "node_modules/concurrently": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
+ "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "date-fns": "^2.30.0",
+ "lodash": "^4.17.21",
+ "rxjs": "^7.8.1",
+ "shell-quote": "^1.8.1",
+ "spawn-command": "0.0.2",
+ "supports-color": "^8.1.1",
+ "tree-kill": "^1.2.2",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": "^14.13.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/concurrently/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -6305,6 +6371,22 @@
"node": ">=12"
}
},
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
"node_modules/dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
@@ -13096,6 +13178,15 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
"node_modules/safe-array-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
@@ -13259,6 +13350,15 @@
"node": ">=8"
}
},
+ "node_modules/shell-quote": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
+ "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -13338,6 +13438,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/spawn-command": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
+ "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
+ "dev": true
+ },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -13893,6 +13999,15 @@
"node": ">=12"
}
},
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -14098,9 +14213,9 @@
}
},
"node_modules/typescript": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
- "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
+ "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
diff --git a/package.json b/package.json
index e44d17c..babb954 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "core",
"version": "1.0.0",
+ "workspaces": ["content-schema"],
"private": true,
"scripts": {
"postinstall": "patch-package",
@@ -12,7 +13,8 @@
"test": "jest",
"prettier": "prettier -w .",
"test:e2e": "npx prisma migrate reset --force && playwright test",
- "upload-content-schema": "ts-node --project tsconfig.script.json ./scripts/upload-content-schema https://raw.githubusercontent.com/micro-course/content-schema/main/schemas/ ./src/shared/api/content/schemas"
+ "upload-content-schema": "ts-node --project tsconfig.script.json ./scripts/upload-content-schema https://raw.githubusercontent.com/ArturW1998/course-app/main/content-schema/schemas/ ./src/shared/api/content/schemas",
+ "dev:content-schema": "npm run dev --workspace=@core/content-schema"
},
"prisma": {
"seed": "ts-node --project tsconfig.script.json prisma/seed.ts"