From 21e794d3d532947ed69e0a19246d67f53ad5d668 Mon Sep 17 00:00:00 2001 From: arturvoloshyn Date: Fri, 8 Mar 2024 23:06:39 +0200 Subject: [PATCH] feat(content-schema): add content-schema --- content-schema/.gitignore | 2 + content-schema/README.md | 1 + .../example/courses/test-course-1/course.yaml | 14 ++ .../lessons/lesson-1/lesson.yaml | 45 ++++++ content-schema/example/manifeset.yaml | 3 + content-schema/package.json | 31 ++++ content-schema/schemas/course.schema.d.ts | 28 ++++ content-schema/schemas/course.schema.json | 85 +++++++++++ content-schema/schemas/lesson.schema.d.ts | 38 +++++ content-schema/schemas/lesson.schema.json | 106 ++++++++++++++ content-schema/schemas/manifest.schema.d.ts | 10 ++ content-schema/schemas/manifest.schema.json | 14 ++ .../scripts/generate-schema-types.ts | 41 ++++++ content-schema/scripts/replace-cuid.ts | 40 ++++++ content-schema/scripts/validate.ts | 136 ++++++++++++++++++ content-schema/tsconfig.json | 21 +++ package-lock.json | 121 +++++++++++++++- package.json | 4 +- 18 files changed, 736 insertions(+), 4 deletions(-) create mode 100644 content-schema/.gitignore create mode 100644 content-schema/README.md create mode 100644 content-schema/example/courses/test-course-1/course.yaml create mode 100644 content-schema/example/courses/test-course-1/lessons/lesson-1/lesson.yaml create mode 100644 content-schema/example/manifeset.yaml create mode 100644 content-schema/package.json create mode 100644 content-schema/schemas/course.schema.d.ts create mode 100644 content-schema/schemas/course.schema.json create mode 100644 content-schema/schemas/lesson.schema.d.ts create mode 100644 content-schema/schemas/lesson.schema.json create mode 100644 content-schema/schemas/manifest.schema.d.ts create mode 100644 content-schema/schemas/manifest.schema.json create mode 100644 content-schema/scripts/generate-schema-types.ts create mode 100644 content-schema/scripts/replace-cuid.ts create mode 100644 content-schema/scripts/validate.ts create mode 100644 content-schema/tsconfig.json 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"