diff --git a/.github/scripts/format-studio-release-notes.py b/.github/scripts/format-studio-release-notes.py new file mode 100644 index 0000000000..c42beb5b59 --- /dev/null +++ b/.github/scripts/format-studio-release-notes.py @@ -0,0 +1,68 @@ +import os +import re + +raw = os.environ.get("RAW_NOTES", "") +lines = raw.splitlines() + +output = [] +seen = set() + + +def clean_line(text: str) -> str: + text = text.strip() + if not text: + return "" + + # Drop markdown headers/changelog lines. + if re.match(r"^#+\s", text, flags=re.I): + return "" + if re.match(r"^\*\*?\s*full\s+changelog", text, flags=re.I): + return "" + if re.match(r"^full\s+changelog", text, flags=re.I): + return "" + + text = re.sub(r"^[\s\-*•]+", "", text) + text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s+in\s+https?://\S+", "", text) + text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s*$", "", text) + text = re.sub(r"@([a-zA-Z0-9_-]+)", "", text) + text = re.sub(r"\s*\([^)]*#\d+\)\s*$", "", text) + text = re.sub(r"\s+#\d+\s*$", "", text) + text = re.sub(r"\s+", " ", text).strip(" .:-") + + if len(text) < 8: + return "" + + # Make user-friendly short title + summary when possible. + if ":" in text: + left, right = [p.strip() for p in text.split(":", 1)] + left = left[:40].rstrip(" .") + right_words = right.split() + right = " ".join(right_words[:14]).rstrip(" .") + text = f"{left}: {right}" if right else left + else: + words = text.split() + if len(words) > 16: + text = " ".join(words[:16]).rstrip(" .") + + return text + + +for line in lines: + cleaned = clean_line(line) + if not cleaned: + continue + + # Normalize casing and deduplicate. + cleaned = cleaned[0].upper() + cleaned[1:] if cleaned else cleaned + key = cleaned.lower() + if key in seen: + continue + seen.add(key) + + output.append(f"* {cleaned}") + if len(output) >= 8: + break + +os.makedirs(".tmp", exist_ok=True) +with open(".tmp/final-notes.txt", "w", encoding="utf-8") as f: + f.write("\n".join(output)) diff --git a/.github/scripts/update-studio-version-mapping.py b/.github/scripts/update-studio-version-mapping.py new file mode 100644 index 0000000000..d22ee65574 --- /dev/null +++ b/.github/scripts/update-studio-version-mapping.py @@ -0,0 +1,117 @@ +import os +import re +from packaging.version import Version, InvalidVersion + +studio_ver = os.environ["STUDIO_VERSION"] +abp_ver = os.environ["ABP_VERSION"] +file_path = "docs/en/studio/version-mapping.md" + +try: + studio = Version(studio_ver) +except InvalidVersion: + print(f"❌ Invalid Studio version: {studio_ver}") + raise SystemExit(1) + +with open(file_path, "r") as f: + lines = f.readlines() + +# Find table start (skip SEO and headers) +table_start = 0 +table_end = 0 +for i, line in enumerate(lines): + if line.strip().startswith("|") and "**ABP Studio Version**" in line: + table_start = i + elif table_start > 0 and line.strip() and not line.strip().startswith("|"): + table_end = i + break + +if table_start == 0: + print("❌ Could not find version mapping table") + raise SystemExit(1) + +# If no end found, table goes to end of file +if table_end == 0: + table_end = len(lines) + +# Extract sections +before_table = lines[:table_start] +table_header = lines[table_start : table_start + 2] +data_rows = [l for l in lines[table_start + 2 : table_end] if l.strip().startswith("|")] +after_table = lines[table_end:] + +new_rows = [] +handled = False + + +def parse_version_range(version_str): + """Parse '2.1.5 - 2.1.9' or '2.1.5' into (start, end)""" + version_str = version_str.strip() + + if "–" in version_str or "-" in version_str: + parts = re.split(r"\s*[–-]\s*", version_str) + if len(parts) == 2: + try: + return Version(parts[0].strip()), Version(parts[1].strip()) + except InvalidVersion: + return None, None + + try: + v = Version(version_str) + return v, v + except InvalidVersion: + return None, None + + +def format_row(studio_range, abp_version): + """Format a table row with proper spacing""" + return f"| {studio_range:<22} | {abp_version:<27} |\n" + + +# Process existing rows +for row in data_rows: + match = re.match(r"\|\s*(.+?)\s*\|\s*(.+?)\s*\|", row) + if not match: + continue + + existing_studio_range = match.group(1).strip() + existing_abp = match.group(2).strip() + + if existing_abp != abp_ver: + new_rows.append(row) + continue + + start_ver, end_ver = parse_version_range(existing_studio_range) + + if start_ver is None or end_ver is None: + new_rows.append(row) + continue + + if start_ver <= studio <= end_ver: + print(f"✅ Studio version {studio_ver} already covered in range {existing_studio_range}") + handled = True + new_rows.append(row) + elif end_ver < studio: + if ( + start_ver.major == studio.major + and start_ver.minor == studio.minor + and studio.micro <= end_ver.micro + 5 + ): + new_range = f"{start_ver} - {studio}" + new_rows.append(format_row(new_range, abp_ver)) + print(f"✅ Extended range: {new_range}") + handled = True + else: + new_rows.append(row) + else: + new_rows.append(row) + +if not handled: + new_row = format_row(str(studio), abp_ver) + new_rows.insert(0, new_row) + print(f"✅ Added new mapping: {studio_ver} -> {abp_ver}") + +with open(file_path, "w") as f: + f.writelines(before_table) + f.writelines(table_header) + f.writelines(new_rows) + f.writelines(after_table) diff --git a/.github/workflows/update-studio-docs.yml b/.github/workflows/update-studio-docs.yml index fd258051e6..1a5f6492c3 100644 --- a/.github/workflows/update-studio-docs.yml +++ b/.github/workflows/update-studio-docs.yml @@ -164,6 +164,17 @@ jobs: git checkout -B "$BRANCH" echo "BRANCH=$BRANCH" >> $GITHUB_ENV + # ------------------------------------------------- + # Install helper scripts (embedded; target branch may not have them). + # Source of truth: .github/scripts/*.py in this repository. + # ------------------------------------------------- + - name: Install workflow scripts + run: | + mkdir -p .github/scripts .tmp + echo "aW1wb3J0IG9zCmltcG9ydCByZQoKcmF3ID0gb3MuZW52aXJvbi5nZXQoIlJBV19OT1RFUyIsICIiKQpsaW5lcyA9IHJhdy5zcGxpdGxpbmVzKCkKCm91dHB1dCA9IFtdCnNlZW4gPSBzZXQoKQoKCmRlZiBjbGVhbl9saW5lKHRleHQ6IHN0cikgLT4gc3RyOgogICAgdGV4dCA9IHRleHQuc3RyaXAoKQogICAgaWYgbm90IHRleHQ6CiAgICAgICAgcmV0dXJuICIiCgogICAgIyBEcm9wIG1hcmtkb3duIGhlYWRlcnMvY2hhbmdlbG9nIGxpbmVzLgogICAgaWYgcmUubWF0Y2gociJeIytccyIsIHRleHQsIGZsYWdzPXJlLkkpOgogICAgICAgIHJldHVybiAiIgogICAgaWYgcmUubWF0Y2gociJeXCpcKj9ccypmdWxsXHMrY2hhbmdlbG9nIiwgdGV4dCwgZmxhZ3M9cmUuSSk6CiAgICAgICAgcmV0dXJuICIiCiAgICBpZiByZS5tYXRjaChyIl5mdWxsXHMrY2hhbmdlbG9nIiwgdGV4dCwgZmxhZ3M9cmUuSSk6CiAgICAgICAgcmV0dXJuICIiCgogICAgdGV4dCA9IHJlLnN1YihyIl5bXHNcLSrigKJdKyIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIlxzK2J5XHMrQD9bYS16QS1aMC05Xy1dK1xzK2luXHMraHR0cHM/Oi8vXFMrIiwgIiIsIHRleHQpCiAgICB0ZXh0ID0gcmUuc3ViKHIiXHMrYnlccytAP1thLXpBLVowLTlfLV0rXHMqJCIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIkAoW2EtekEtWjAtOV8tXSspIiwgIiIsIHRleHQpCiAgICB0ZXh0ID0gcmUuc3ViKHIiXHMqXChbXildKiNcZCtcKVxzKiQiLCAiIiwgdGV4dCkKICAgIHRleHQgPSByZS5zdWIociJccysjXGQrXHMqJCIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIlxzKyIsICIgIiwgdGV4dCkuc3RyaXAoIiAuOi0iKQoKICAgIGlmIGxlbih0ZXh0KSA8IDg6CiAgICAgICAgcmV0dXJuICIiCgogICAgIyBNYWtlIHVzZXItZnJpZW5kbHkgc2hvcnQgdGl0bGUgKyBzdW1tYXJ5IHdoZW4gcG9zc2libGUuCiAgICBpZiAiOiIgaW4gdGV4dDoKICAgICAgICBsZWZ0LCByaWdodCA9IFtwLnN0cmlwKCkgZm9yIHAgaW4gdGV4dC5zcGxpdCgiOiIsIDEpXQogICAgICAgIGxlZnQgPSBsZWZ0Wzo0MF0ucnN0cmlwKCIgLiIpCiAgICAgICAgcmlnaHRfd29yZHMgPSByaWdodC5zcGxpdCgpCiAgICAgICAgcmlnaHQgPSAiICIuam9pbihyaWdodF93b3Jkc1s6MTRdKS5yc3RyaXAoIiAuIikKICAgICAgICB0ZXh0ID0gZiJ7bGVmdH06IHtyaWdodH0iIGlmIHJpZ2h0IGVsc2UgbGVmdAogICAgZWxzZToKICAgICAgICB3b3JkcyA9IHRleHQuc3BsaXQoKQogICAgICAgIGlmIGxlbih3b3JkcykgPiAxNjoKICAgICAgICAgICAgdGV4dCA9ICIgIi5qb2luKHdvcmRzWzoxNl0pLnJzdHJpcCgiIC4iKQoKICAgIHJldHVybiB0ZXh0CgoKZm9yIGxpbmUgaW4gbGluZXM6CiAgICBjbGVhbmVkID0gY2xlYW5fbGluZShsaW5lKQogICAgaWYgbm90IGNsZWFuZWQ6CiAgICAgICAgY29udGludWUKCiAgICAjIE5vcm1hbGl6ZSBjYXNpbmcgYW5kIGRlZHVwbGljYXRlLgogICAgY2xlYW5lZCA9IGNsZWFuZWRbMF0udXBwZXIoKSArIGNsZWFuZWRbMTpdIGlmIGNsZWFuZWQgZWxzZSBjbGVhbmVkCiAgICBrZXkgPSBjbGVhbmVkLmxvd2VyKCkKICAgIGlmIGtleSBpbiBzZWVuOgogICAgICAgIGNvbnRpbnVlCiAgICBzZWVuLmFkZChrZXkpCgogICAgb3V0cHV0LmFwcGVuZChmIioge2NsZWFuZWR9IikKICAgIGlmIGxlbihvdXRwdXQpID49IDg6CiAgICAgICAgYnJlYWsKCm9zLm1ha2VkaXJzKCIudG1wIiwgZXhpc3Rfb2s9VHJ1ZSkKd2l0aCBvcGVuKCIudG1wL2ZpbmFsLW5vdGVzLnR4dCIsICJ3IiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgIGYud3JpdGUoIlxuIi5qb2luKG91dHB1dCkpCg==" | base64 -d > .github/scripts/format-studio-release-notes.py + echo "aW1wb3J0IG9zCmltcG9ydCByZQpmcm9tIHBhY2thZ2luZy52ZXJzaW9uIGltcG9ydCBWZXJzaW9uLCBJbnZhbGlkVmVyc2lvbgoKc3R1ZGlvX3ZlciA9IG9zLmVudmlyb25bIlNUVURJT19WRVJTSU9OIl0KYWJwX3ZlciA9IG9zLmVudmlyb25bIkFCUF9WRVJTSU9OIl0KZmlsZV9wYXRoID0gImRvY3MvZW4vc3R1ZGlvL3ZlcnNpb24tbWFwcGluZy5tZCIKCnRyeToKICAgIHN0dWRpbyA9IFZlcnNpb24oc3R1ZGlvX3ZlcikKZXhjZXB0IEludmFsaWRWZXJzaW9uOgogICAgcHJpbnQoZiLinYwgSW52YWxpZCBTdHVkaW8gdmVyc2lvbjoge3N0dWRpb192ZXJ9IikKICAgIHJhaXNlIFN5c3RlbUV4aXQoMSkKCndpdGggb3BlbihmaWxlX3BhdGgsICJyIikgYXMgZjoKICAgIGxpbmVzID0gZi5yZWFkbGluZXMoKQoKIyBGaW5kIHRhYmxlIHN0YXJ0IChza2lwIFNFTyBhbmQgaGVhZGVycykKdGFibGVfc3RhcnQgPSAwCnRhYmxlX2VuZCA9IDAKZm9yIGksIGxpbmUgaW4gZW51bWVyYXRlKGxpbmVzKToKICAgIGlmIGxpbmUuc3RyaXAoKS5zdGFydHN3aXRoKCJ8IikgYW5kICIqKkFCUCBTdHVkaW8gVmVyc2lvbioqIiBpbiBsaW5lOgogICAgICAgIHRhYmxlX3N0YXJ0ID0gaQogICAgZWxpZiB0YWJsZV9zdGFydCA+IDAgYW5kIGxpbmUuc3RyaXAoKSBhbmQgbm90IGxpbmUuc3RyaXAoKS5zdGFydHN3aXRoKCJ8Iik6CiAgICAgICAgdGFibGVfZW5kID0gaQogICAgICAgIGJyZWFrCgppZiB0YWJsZV9zdGFydCA9PSAwOgogICAgcHJpbnQoIuKdjCBDb3VsZCBub3QgZmluZCB2ZXJzaW9uIG1hcHBpbmcgdGFibGUiKQogICAgcmFpc2UgU3lzdGVtRXhpdCgxKQoKIyBJZiBubyBlbmQgZm91bmQsIHRhYmxlIGdvZXMgdG8gZW5kIG9mIGZpbGUKaWYgdGFibGVfZW5kID09IDA6CiAgICB0YWJsZV9lbmQgPSBsZW4obGluZXMpCgojIEV4dHJhY3Qgc2VjdGlvbnMKYmVmb3JlX3RhYmxlID0gbGluZXNbOnRhYmxlX3N0YXJ0XQp0YWJsZV9oZWFkZXIgPSBsaW5lc1t0YWJsZV9zdGFydCA6IHRhYmxlX3N0YXJ0ICsgMl0KZGF0YV9yb3dzID0gW2wgZm9yIGwgaW4gbGluZXNbdGFibGVfc3RhcnQgKyAyIDogdGFibGVfZW5kXSBpZiBsLnN0cmlwKCkuc3RhcnRzd2l0aCgifCIpXQphZnRlcl90YWJsZSA9IGxpbmVzW3RhYmxlX2VuZDpdCgpuZXdfcm93cyA9IFtdCmhhbmRsZWQgPSBGYWxzZQoKCmRlZiBwYXJzZV92ZXJzaW9uX3JhbmdlKHZlcnNpb25fc3RyKToKICAgICIiIlBhcnNlICcyLjEuNSAtIDIuMS45JyBvciAnMi4xLjUnIGludG8gKHN0YXJ0LCBlbmQpIiIiCiAgICB2ZXJzaW9uX3N0ciA9IHZlcnNpb25fc3RyLnN0cmlwKCkKCiAgICBpZiAi4oCTIiBpbiB2ZXJzaW9uX3N0ciBvciAiLSIgaW4gdmVyc2lvbl9zdHI6CiAgICAgICAgcGFydHMgPSByZS5zcGxpdChyIlxzKlvigJMtXVxzKiIsIHZlcnNpb25fc3RyKQogICAgICAgIGlmIGxlbihwYXJ0cykgPT0gMjoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgcmV0dXJuIFZlcnNpb24ocGFydHNbMF0uc3RyaXAoKSksIFZlcnNpb24ocGFydHNbMV0uc3RyaXAoKSkKICAgICAgICAgICAgZXhjZXB0IEludmFsaWRWZXJzaW9uOgogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUsIE5vbmUKCiAgICB0cnk6CiAgICAgICAgdiA9IFZlcnNpb24odmVyc2lvbl9zdHIpCiAgICAgICAgcmV0dXJuIHYsIHYKICAgIGV4Y2VwdCBJbnZhbGlkVmVyc2lvbjoKICAgICAgICByZXR1cm4gTm9uZSwgTm9uZQoKCmRlZiBmb3JtYXRfcm93KHN0dWRpb19yYW5nZSwgYWJwX3ZlcnNpb24pOgogICAgIiIiRm9ybWF0IGEgdGFibGUgcm93IHdpdGggcHJvcGVyIHNwYWNpbmciIiIKICAgIHJldHVybiBmInwge3N0dWRpb19yYW5nZTo8MjJ9IHwge2FicF92ZXJzaW9uOjwyN30gfFxuIgoKCiMgUHJvY2VzcyBleGlzdGluZyByb3dzCmZvciByb3cgaW4gZGF0YV9yb3dzOgogICAgbWF0Y2ggPSByZS5tYXRjaChyIlx8XHMqKC4rPylccypcfFxzKiguKz8pXHMqXHwiLCByb3cpCiAgICBpZiBub3QgbWF0Y2g6CiAgICAgICAgY29udGludWUKCiAgICBleGlzdGluZ19zdHVkaW9fcmFuZ2UgPSBtYXRjaC5ncm91cCgxKS5zdHJpcCgpCiAgICBleGlzdGluZ19hYnAgPSBtYXRjaC5ncm91cCgyKS5zdHJpcCgpCgogICAgaWYgZXhpc3RpbmdfYWJwICE9IGFicF92ZXI6CiAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKHJvdykKICAgICAgICBjb250aW51ZQoKICAgIHN0YXJ0X3ZlciwgZW5kX3ZlciA9IHBhcnNlX3ZlcnNpb25fcmFuZ2UoZXhpc3Rpbmdfc3R1ZGlvX3JhbmdlKQoKICAgIGlmIHN0YXJ0X3ZlciBpcyBOb25lIG9yIGVuZF92ZXIgaXMgTm9uZToKICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQogICAgICAgIGNvbnRpbnVlCgogICAgaWYgc3RhcnRfdmVyIDw9IHN0dWRpbyA8PSBlbmRfdmVyOgogICAgICAgIHByaW50KGYi4pyFIFN0dWRpbyB2ZXJzaW9uIHtzdHVkaW9fdmVyfSBhbHJlYWR5IGNvdmVyZWQgaW4gcmFuZ2Uge2V4aXN0aW5nX3N0dWRpb19yYW5nZX0iKQogICAgICAgIGhhbmRsZWQgPSBUcnVlCiAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKHJvdykKICAgIGVsaWYgZW5kX3ZlciA8IHN0dWRpbzoKICAgICAgICBpZiAoCiAgICAgICAgICAgIHN0YXJ0X3Zlci5tYWpvciA9PSBzdHVkaW8ubWFqb3IKICAgICAgICAgICAgYW5kIHN0YXJ0X3Zlci5taW5vciA9PSBzdHVkaW8ubWlub3IKICAgICAgICAgICAgYW5kIHN0dWRpby5taWNybyA8PSBlbmRfdmVyLm1pY3JvICsgNQogICAgICAgICk6CiAgICAgICAgICAgIG5ld19yYW5nZSA9IGYie3N0YXJ0X3Zlcn0gLSB7c3R1ZGlvfSIKICAgICAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKGZvcm1hdF9yb3cobmV3X3JhbmdlLCBhYnBfdmVyKSkKICAgICAgICAgICAgcHJpbnQoZiLinIUgRXh0ZW5kZWQgcmFuZ2U6IHtuZXdfcmFuZ2V9IikKICAgICAgICAgICAgaGFuZGxlZCA9IFRydWUKICAgICAgICBlbHNlOgogICAgICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQogICAgZWxzZToKICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQoKaWYgbm90IGhhbmRsZWQ6CiAgICBuZXdfcm93ID0gZm9ybWF0X3JvdyhzdHIoc3R1ZGlvKSwgYWJwX3ZlcikKICAgIG5ld19yb3dzLmluc2VydCgwLCBuZXdfcm93KQogICAgcHJpbnQoZiLinIUgQWRkZWQgbmV3IG1hcHBpbmc6IHtzdHVkaW9fdmVyfSAtPiB7YWJwX3Zlcn0iKQoKd2l0aCBvcGVuKGZpbGVfcGF0aCwgInciKSBhcyBmOgogICAgZi53cml0ZWxpbmVzKGJlZm9yZV90YWJsZSkKICAgIGYud3JpdGVsaW5lcyh0YWJsZV9oZWFkZXIpCiAgICBmLndyaXRlbGluZXMobmV3X3Jvd3MpCiAgICBmLndyaXRlbGluZXMoYWZ0ZXJfdGFibGUpCg==" | base64 -d > .github/scripts/update-studio-version-mapping.py + ls -la .github/scripts/ + # ------------------------------------------------- # Analyze existing release notes format # ------------------------------------------------- @@ -242,74 +253,7 @@ jobs: echo "$AI_RESPONSE" > .tmp/final-notes.txt else echo "⚠️ AI unavailable - generating concise user-friendly summaries from raw notes" - - python3 <<'PYTHON_EOF' - import os - import re - - raw = os.environ.get("RAW_NOTES", "") - lines = raw.splitlines() - - output = [] - seen = set() - - def clean_line(text: str) -> str: - text = text.strip() - if not text: - return "" - - # Drop markdown headers/changelog lines. - if re.match(r"^#+\s", text, flags=re.I): - return "" - if re.match(r"^\*\*?\s*full\s+changelog", text, flags=re.I): - return "" - if re.match(r"^full\s+changelog", text, flags=re.I): - return "" - - text = re.sub(r"^[\s\-*•]+", "", text) - text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s+in\s+https?://\S+", "", text) - text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s*$", "", text) - text = re.sub(r"@([a-zA-Z0-9_-]+)", "", text) - text = re.sub(r"\s*\([^)]*#\d+\)\s*$", "", text) - text = re.sub(r"\s+#\d+\s*$", "", text) - text = re.sub(r"\s+", " ", text).strip(" .:-") - - if len(text) < 8: - return "" - - # Make user-friendly short title + summary when possible. - if ":" in text: - left, right = [p.strip() for p in text.split(":", 1)] - left = left[:40].rstrip(" .") - right_words = right.split() - right = " ".join(right_words[:14]).rstrip(" .") - text = f"{left}: {right}" if right else left - else: - words = text.split() - if len(words) > 16: - text = " ".join(words[:16]).rstrip(" .") - - return text - - for line in lines: - cleaned = clean_line(line) - if not cleaned: - continue - - # Normalize casing and deduplicate. - cleaned = cleaned[0].upper() + cleaned[1:] if cleaned else cleaned - key = cleaned.lower() - if key in seen: - continue - seen.add(key) - - output.append(f"* {cleaned}") - if len(output) >= 8: - break - - with open('.tmp/final-notes.txt', 'w', encoding='utf-8') as f: - f.write("\n".join(output)) - PYTHON_EOF + python3 .github/scripts/format-studio-release-notes.py fi # Normalize bullets to "* " even if AI returns "- ". @@ -454,137 +398,8 @@ jobs: exit 0 fi - # Use Python for smart version range handling - python3 <<'PYTHON_EOF' - import os - import re - from packaging.version import Version, InvalidVersion - - studio_ver = os.environ["STUDIO_VERSION"] - abp_ver = os.environ["ABP_VERSION"] - file_path = "docs/en/studio/version-mapping.md" - - try: - studio = Version(studio_ver) - except InvalidVersion: - print(f"❌ Invalid Studio version: {studio_ver}") - exit(1) - - with open(file_path, 'r') as f: - lines = f.readlines() - - # Find table start (skip SEO and headers) - table_start = 0 - table_end = 0 - for i, line in enumerate(lines): - if line.strip().startswith('|') and '**ABP Studio Version**' in line: - table_start = i - elif table_start > 0 and line.strip() and not line.strip().startswith('|'): - table_end = i - break - - if table_start == 0: - print("❌ Could not find version mapping table") - exit(1) - - # If no end found, table goes to end of file - if table_end == 0: - table_end = len(lines) - - # Extract sections - before_table = lines[:table_start] # Everything before table - table_header = lines[table_start:table_start+2] # Header + separator - data_rows = [l for l in lines[table_start+2:table_end] if l.strip().startswith('|')] # Data rows - after_table = lines[table_end:] # Everything after table + python3 .github/scripts/update-studio-version-mapping.py - new_rows = [] - handled = False - - def parse_version_range(version_str): - """Parse '2.1.5 - 2.1.9' or '2.1.5' into (start, end)""" - version_str = version_str.strip() - - if '–' in version_str or '-' in version_str: - # Handle both em-dash and hyphen - parts = re.split(r'\s*[–-]\s*', version_str) - if len(parts) == 2: - try: - return Version(parts[0].strip()), Version(parts[1].strip()) - except InvalidVersion: - return None, None - - try: - v = Version(version_str) - return v, v - except InvalidVersion: - return None, None - - def format_row(studio_range, abp_version): - """Format a table row with proper spacing""" - return f"| {studio_range:<22} | {abp_version:<27} |\n" - - # Process existing rows - for row in data_rows: - match = re.match(r'\|\s*(.+?)\s*\|\s*(.+?)\s*\|', row) - if not match: - continue - - existing_studio_range = match.group(1).strip() - existing_abp = match.group(2).strip() - - # Only consider rows with matching ABP version - if existing_abp != abp_ver: - new_rows.append(row) - continue - - start_ver, end_ver = parse_version_range(existing_studio_range) - - if start_ver is None or end_ver is None: - new_rows.append(row) - continue - - # Check if current studio version is in this range - if start_ver <= studio <= end_ver: - print(f"✅ Studio version {studio_ver} already covered in range {existing_studio_range}") - handled = True - new_rows.append(row) - - # Check if we should extend the range - elif end_ver < studio: - # Calculate if studio is the next logical version - # For patch versions: 2.1.9 -> 2.1.10 - # For minor versions: 2.1.9 -> 2.2.0 - - # Simple heuristic: if major.minor match and patch increments, extend range - if (start_ver.major == studio.major and - start_ver.minor == studio.minor and - studio.micro <= end_ver.micro + 5): # Allow small gaps - - new_range = f"{start_ver} - {studio}" - new_rows.append(format_row(new_range, abp_ver)) - print(f"✅ Extended range: {new_range}") - handled = True - else: - new_rows.append(row) - else: - new_rows.append(row) - - # If not handled, add new row at top of data - if not handled: - new_row = format_row(str(studio), abp_ver) - new_rows.insert(0, new_row) - print(f"✅ Added new mapping: {studio_ver} -> {abp_ver}") - - # Write updated file - preserve ALL content - with open(file_path, 'w') as f: - f.writelines(before_table) # SEO, title, intro text - f.writelines(table_header) # Table header - f.writelines(new_rows) # Updated data rows - f.writelines(after_table) # Content after table (preview section, etc.) - - print("MAPPING_UPDATED=true") - PYTHON_EOF - echo "MAPPING_UPDATED=true" >> $GITHUB_ENV echo "=== Updated version-mapping.md preview ===" diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/POST.md b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/POST.md new file mode 100644 index 0000000000..1440f5061d --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/POST.md @@ -0,0 +1,143 @@ +# Deep Dive on ABP AI Agent #3: Rules, Skills and Lessons + +Every new chat starts from zero. + +The model can write the API, the permissions, and the UI. What disappears between sessions is everything specific to *this* solution: your naming rules, your feature checklist, the demo-data fix from yesterday. You end up re-teaching before you start building. + +That is not an intelligence problem. The model is already capable enough. What does not carry over is the setup around it. + +## Agent, Model, and Harness + +Three terms get mixed up constantly. Keeping them separate makes everything else in this article click. + +**The model** is the LLM: the weights, the training data, the reasoning engine. You do not get to change it. You pick which one to use, and that is about it. + +**The harness** is everything wrapped around the model so it can actually finish work in *your* environment: the system prompt, the tools it can call, the files it may read, the checks that run on its output, the limits on what it may touch, and the instructions injected into every session. A raw model is not an agent. An agent is the model plus the harness. If you are not the model, you are building the harness. + +**The agent** is the whole system: model plus harness, running in a loop until the task is done. The behaviour you experience is dominated by the harness, not just the model. [Addy Osmani](https://addyosmani.com/blog/agent-harness-engineering/) puts the same idea plainly: *a decent model with a great harness beats a great model with a bad harness.* The gap between what today's models can do and what you actually see them do in your codebase is mostly a harness gap. + +The habit that makes a harness good is simple: treat every mistake as a reason to update the setup, not just to fix the output in chat. Each time the agent gets something wrong, change what it always knows, can look up, or carries forward so it cannot repeat the same error. Do that consistently and the agent stops repeating mistakes and starts working the way your team works. + +In **ABP Studio AI Coding Agent**, the harness includes three pieces that hold what the agent knows about your solution: **Rules**, **Skills**, and **Lessons**. They live under **Settings > Rules & Skills**. + +![The Rules & Skills settings page in ABP Studio](rules-and-skills-settings.png) + +## Rules: The House Rules On The Wall + +A **Rule** is a standing instruction that the harness injects into the agent's context on every turn of every session. In harness-engineering terms, it is always-on context injection: the same role as an `AGENTS.md` or `CLAUDE.md` at the root of a repo, except scoped to your solution or your profile inside ABP Studio. + +Think of the house rules pinned to a kitchen wall: how we do things here, every shift, no exceptions. Unlike a glance at a poster, though, a Rule is not ambient background. It is read on every turn, so it costs context budget every time the agent acts. + +ABP Agent already follows the framework's own conventions by default. It goes through the data layer instead of hitting the database directly, uses the built-in permission system instead of hand-rolled checks, and reads user-facing text from the translation files instead of hardcoding it. You do not write those down. + +Rules are where *your* conventions go. The ones the framework cannot guess because they belong to your solution and your team. The good part is that these read like plain conventions any developer would recognize, not framework trivia: + +* New code goes in the same place, with the same naming, as the code already around it. +* Every endpoint that changes data checks permissions before it does anything. +* Money is stored in minor units (cents), never as a floating-point number. +* API errors use our standard response shape, never ad-hoc JSON. + +With those on the wall, I stop repeating them. Instead of writing this: + +```text +Add a Category screen. Use our data layer, do not query the database +directly. Check permissions on every write. Keep money in cents. Put the +files where the other features live. +``` + +I can write this: + +```text +Add a Category screen. +``` + +The conventions are not renegotiated, one prompt at a time, in every session. The agent applies them the way a teammate who has read the contributor guide would. + +This matters more than it looks. A model with no standing rules does not stay neutral. It falls back on the defaults from its training data. Ask a generic model for "an endpoint that returns categories" and you often get one with the database query written straight into the handler, because that is the most common shape on the public internet. Rules steer the agent's output back toward *your* codebase. + +One caution: treat the rule list like a pilot's checklist, not a style guide. A Rule is injected on every turn, so keep the list short. Every line should earn its place, ideally traceable to a real failure or a hard constraint you cannot ignore. If a line does not change what the agent produces, it is noise, and it dilutes the rules that actually matter. + +You also choose where a rule is saved. Global rules sit under your user profile and apply to every solution on your machine. Solution rules apply only to the current solution, and only while it is open. + +## Skills: Recipes You Pull Off The Shelf + +A **Skill** is the same kind of note as a Rule, but loaded only when the task calls for it. The harness keeps a short description of each skill in context at all times; the agent reads the full text only when that description matches what you asked it to do. Harness engineers call this **progressive disclosure**: reveal instructions and detail only when they are needed, instead of stuffing everything into the opening prompt. + +A recipe is not pinned to the wall. It sits in a drawer, and the cook pulls it out only when making that dish. That is a Skill. + +In ABP Studio, a rule and a skill are the same kind of note with one switch between them. + +![Creating a rule or a skill: the Always Apply toggle decides which one it is](create-rule-skill-dialog.png) + +You write a name (which becomes the file name) and some content, then set **Always Apply**. Turn it on and the note is a Rule, always in context. Turn it off and the note is a Skill, fetched on demand. So if a skill turns out to be something the agent should never skip, you flip one switch and it becomes a rule. + +My favorite example of a skill is a "build a feature end to end" checklist: the ordered steps your team follows to ship one complete feature. The data model comes first, then the data access layer, then the application service, then the API, then the translations, then the UI, and finally the demo data. Write it once, and the agent follows it whenever it builds a feature, instead of inventing its own order each time. + +Writing a skill is like writing an onboarding note for a new teammate. You are not making them smarter. You are saving them the week it would take to work out how your team does this one thing. + +A skill can be long without slowing anything down. The agent sees only the short description of each skill at all times, and reads the full text only when the description matches the task. The detail stays in the drawer until it is needed. + +Skills are also portable. A skill is plain Markdown, so you can import one written elsewhere instead of retyping it. + +![Importing skills from .md files into a solution or your global profile](import-rules.png) + +You point the importer at a file or a folder of `.md` files and choose whether they go into the current solution or your global profile. A procedure one person worked out can travel to the rest of the team, or to your next solution. + +A simple test for which one to reach for: + +* If it should hold no matter what you are doing, it is a **Rule**. ("Always return errors in our standard shape.") +* If it is a procedure you follow only for a certain kind of task, it is a **Skill**. ("Here is our checklist for adding a feature end to end.") + +## Lessons: What The Agent Learns On Its Own + +Rules and Skills are written by a person. **Lessons** are written by the agent. + +Think of a shift handoff log in the kitchen: after something goes wrong, someone writes down what happened and what to do differently next time. The next cook reads it before repeating the same work. In ABP Studio, you correct the mistake; the agent records the verified fix so future sessions do not trip over it again. That entry is a Lesson. + +When the agent gets something wrong and is corrected, by you, by a failing build, or by the official ABP documentation, it can record the correction as a short, verified note. The harness carries that note into later turns and later sessions as high-priority context. That is the `ai-learned-lessons` entry you saw in the settings list, growing as the agent works in your solution. + +The idea behind it is simple: when the agent gets something wrong, fix it once so it never gets it wrong the same way again. You already do a version of this when you edit a notes file by hand. Lessons do the recording for you, at the moment of the correction, when the reason is still fresh and you would otherwise forget to write it down. + +Here is the shape of a real one. I ask the agent to add a `Category` screen. It builds the data model, the API, and the UI, but forgets to grant the new permission in the demo data. The screen compiles, the API responds, everything looks finished, and the only symptom is that real users get "access denied." I correct it, the agent fixes the demo data, and it records a note: in this solution, a new permission is not done until it is granted in the demo data. Next time, it remembers. + +A Lesson is not a general fact about ABP. The documentation already covers that, and the agent reads it. A Lesson is a solution-specific, verified correction: the kind of knowledge a team usually keeps in people's heads and loses when they move on. Here it is written down the moment it is learned, instead of weeks later, if ever. + +## When To Use Which? + +Once the three are clear, they sort themselves out: + +| Mechanism | Who writes it | When it loads | Answers | +| --- | --- | --- | --- | +| **Rules** | You | Always, every turn | "What must always be true here?" | +| **Skills** | You | When relevant to the task | "How do we do this kind of task?" | +| **Lessons** | The agent | In later sessions, as high-priority context | "What did we get wrong before in this solution?" | + +They feed into each other. A Lesson that keeps coming back is a sign it should be promoted. If the agent records the same correction again and again, it is no longer a one-off note. It belongs in a Rule, if it is an always-true convention, or in a Skill, if it is a procedure. Knowledge tends to move from Lessons toward Rules and Skills: the agent finds the pattern by tripping over it, and you write it down properly once it has earned the spot. + +Keep an eye on the budget, because all three share the same limited space, the context window. Rules cost the most, since they are always present, so an overstuffed rule list weighs down every turn and crowds out the rules that matter. Skills are cheaper, paid for only when used, which is a good reason to move anything situational out of Rules and into a Skill. Lessons add up too, so clear out the stale ones now and then. The point is not to write down everything you know. It is to write down the few things that change what the agent produces, and put each one where it loads at the right time. + +## Why This Is Different From Generic Coding Agents? + +To be fair, the ideas themselves are not unique to ABP. Tools like Cursor, Claude Code, Codex, and Windsurf are strong general-purpose coding tools, and they already have always-on instruction files and on-demand skill files. If all you compare is whether a tool can hold rules and skills, there is no real difference, and I would not pretend otherwise. + +The difference is in the two places where a generic tool has to guess. + +The first is memory of its own mistakes. In most tools, remembering a past mistake means you stop and edit a memory file by hand. Lessons remove that step. The agent records the correction itself, the moment it happens, so the knowledge survives without you having to maintain it. + +The second, and the one that matters most, is what all of this sits on. A generic instruction file can say "use the data layer," but the tool still has to read your files and guess what the data layer is and where it lives. ABP Agent does not guess. It starts every session with an ABP-aware map of your solution, and when it is unsure how something should be done, it checks the official ABP documentation instead of the average of the public internet. So the rules and skills you write are not hints dropped into a tool that barely understands the project. They are backed by one that already knows the framework underneath them. + +There is also a smaller convenience worth a line: a rule and a skill are the same note with one switch between them, scoped to the solution or your profile from the same place, instead of two separate file formats to keep track of. + +## Conclusion + +It is best to see the three as one system, not three switches. + +* **Rules** hold the conventions you refuse to repeat. +* **Skills** hold the procedures you want to reuse. +* **Lessons** hold the corrections the agent learns as it works. + +Together they answer the blank-memory problem. They do not give the model new weights. They inject, at the start of each session, the smallest useful set of instructions so the agent can work as if it has been here before. + +A generic agent with no harness tuning stays roughly as capable on its thousandth task in your codebase as on its first. With Rules, Skills, and Lessons, ABP Agent accumulates solution-specific knowledge over time, especially through Lessons and the promotions you make from them. + +That is the real value: **not just an AI that writes code, but one that learns how your solution is built and keeps that knowledge from one session to the next.** diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/cover-image.png b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/cover-image.png new file mode 100644 index 0000000000..f88dd33b79 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/cover-image.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/create-rule-skill-dialog.png b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/create-rule-skill-dialog.png new file mode 100644 index 0000000000..0d874ae897 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/create-rule-skill-dialog.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/import-rules.png b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/import-rules.png new file mode 100644 index 0000000000..6e5f10dab0 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/import-rules.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/rules-and-skills-settings.png b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/rules-and-skills-settings.png new file mode 100644 index 0000000000..d66fdda94b Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-3-rules-skills-lessons/rules-and-skills-settings.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-mcp-server.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-mcp-server.png new file mode 100644 index 0000000000..766e0f55a2 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-mcp-server.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-seo-analyzer-mcp-server.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-seo-analyzer-mcp-server.png new file mode 100644 index 0000000000..1e98f568da Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/add-seo-analyzer-mcp-server.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/cover-image.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/cover-image.png new file mode 100644 index 0000000000..f4baa39fd2 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/cover-image.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/mcp-servers-empty.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/mcp-servers-empty.png new file mode 100644 index 0000000000..ea4785c009 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/mcp-servers-empty.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/post.md new file mode 100644 index 0000000000..a28a96edcd --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/post.md @@ -0,0 +1,315 @@ +# Deep Dive on ABP AI Agent #5: MCP (Model Context Protocol) + +When I use an AI coding agent inside ABP Studio, most of the work starts inside the solution. + +The agent can read the code. It can edit files. It can use the ABP Studio tools I enabled. It can build the solution, run tasks, start applications, inspect containers, and check monitoring data when something fails. [In the previous article of this](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-be2xa2om) series, we focused on exactly that: the integrated tools that connect the agent to the ABP Studio development environment. + +But there is another point where plain solution awareness is not enough. + +Sometimes the information I need is not in the repository. It is not in the running application. It is not in the build output, container state, or ABP Studio monitoring screen. + +It may be in a live Prometheus workspace. It may be in Google Search Console. It may be in an SEO analyzer, a documentation system, a database, a customer support tool, or another service that has nothing to do with the ABP solution itself. + +That is where **MCP** becomes useful. + +MCP, short for **Model Context Protocol**, gives ABP AI Coding Agent a standard way to reach external tools and data sources. It does not replace the built-in ABP Studio tools. It extends the agent beyond the solution boundary when the task depends on something outside that boundary. + +> **Note:** ABP AI Coding Agent is available directly to ABP license holders. License holders have predefined credits so they can try the integrated experience without setting up a separate AI workflow first. MCP is one of the ways this experience can be extended when the agent needs access to systems outside ABP Studio. + +## Why MCP Matters + +Most coding agents start from the same place: **the files they can see**. + +That is a good start, but real development work often needs context from somewhere else: production metrics, SEO data, analytics, search performance, customer records, internal APIs, or product knowledge. + +If the agent cannot reach those systems, the developer becomes the bridge: + +- Copy this metric result. +- Paste that SEO report. +- Export this analytics data. +- Summarize this dashboard. +- Explain which production signal matters. +- Tell the agent what the external system says. + +That works, but it is not the best workflow. The more information I manually copy into the chat, the easier it is to miss something, simplify too much, or give the agent an outdated snapshot. + +MCP reduces that manual work. It lets the agent ask an external system for context directly, within the permission boundary I configure. + +The important detail is that MCP is not "the agent can do anything now." It is a controlled extension point. A server is connected. Its tools are listed. Individual tools can be enabled or disabled. The agent can only use what is available in the current session. + +That gives me a practical balance: the ABP-aware development experience stays in ABP Studio, and external context can be added only when the task needs it. + +## What MCP Actually Is + +MCP is an open standard for connecting external tools and data to AI agents. + +Instead of every tool inventing its own integration for every agent, MCP defines a common shape. A program that exposes tools or resources through this standard is called an **MCP server**. An agent that understands MCP can connect to that server and use what it provides. + +For me, the easiest way to think about it is simple: + +- ABP Studio tools connect the agent to the ABP development environment. +- MCP servers connect the agent to systems outside that environment. + +The server might expose a tool for querying Prometheus, checking Search Console data, running an SEO audit, inspecting a database, or calling an internal API. The exact capability depends on the server. + +MCP is not a prompt, a rule, a skill, or a lesson. Those shape what the agent knows or how it behaves. MCP changes what the agent can reach. + +That makes MCP more like equipment than instruction. It gives the agent access to a tool or data source that was previously outside its working area. + +There are already many community and vendor MCP servers for different systems. A useful place to discover examples is the [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) repository, which collects MCP servers across many categories. + +For concrete examples, there are MCP servers for [Prometheus metrics](https://github.com/pab1it0/prometheus-mcp-server), [AWS Managed Prometheus](https://github.com/awslabs/mcp/tree/main/src/prometheus-mcp-server), and [SEO analysis](https://github.com/g-battaglia/mcp-seo). The exact server you choose depends on your environment, but the pattern is the same: expose a focused external capability to the agent through MCP. + +## When You Actually Need It + +Most of the time, I would not start with MCP. + +For normal ABP development, the built-in tools are usually the right first layer. The agent can already work with the solution, run profiles, applications, containers, tasks, builds, monitoring data, and documentation. If the task is completely inside the ABP solution, MCP may not add anything. + +MCP becomes useful when the task crosses the solution boundary. + +For example, I may ask: + +```text +Run an SEO audit for https://abp.io, summarize the weak areas, and suggest which content or technical improvements would matter most. +``` + +Without MCP, I would need to open a separate SEO tool, run the audit, copy the results, and paste them into the chat. With the right MCP server enabled, the agent can call the SEO tool directly and reason from the structured result. + +Or I may ask: + +```text +Connect to our Prometheus MCP server, check the error rate and p95 latency for the AuthServer over the last 30 minutes, and tell me whether the last deployment changed anything. +``` + +That is a very different kind of task. The answer is not in the source code alone. It lives in a monitoring system. Prometheus MCP servers exist for this kind of workflow; for example, some expose tools for instant PromQL queries, range queries, metric discovery, and target inspection. + +Another realistic prompt could be: + +```text +Use Search Console data to find pages that lost traffic this month, then inspect the related docs pages and suggest focused improvements. +``` + +Again, the important part is not that the agent magically knows everything. The important part is that I can connect a specific external system, expose a specific set of tools, and let the agent use them when they are relevant. + +If the work starts outside the codebase but ends inside the codebase, MCP can help connect those two parts of the workflow. + +## Setting Up An MCP Server + +MCP servers are configured under **Settings > MCP Servers**. + +When no server is connected, the page is intentionally simple. It is an empty list waiting for the first server. That is important because MCP should be explicit. If I have not connected a server, the agent should not behave as if it has access to that external system. + +![The MCP Servers page in ABP Studio, before any server is added](mcp-servers-empty.png) + +When I add a server, ABP Studio asks how it should connect. + +There are two main connection types: + +- **Stdio:** ABP Studio runs the MCP server as a local process. I provide the command, arguments, and environment variables the server needs. +- **HTTP:** ABP Studio connects to an MCP server over the network. I provide the URL and any required headers. + +![Adding an MCP server: pick stdio or HTTP, then provide the command, arguments, and environment variables](add-mcp-server.png) + +This is useful because different MCP servers are packaged in different ways. Some are local command-line programs. Some are hosted services. Some need environment variables for tokens or configuration. + +In the example below, I am adding an HTTP MCP server named **SEO Analyzer**. It exposes a small set of SEO-related tools through a remote MCP endpoint. + +![Adding the SEO Analyzer MCP server over HTTP](add-seo-analyzer-mcp-server.png) + +ABP Studio does not require every server to be created manually from scratch. If I already use MCP elsewhere, I can import existing configuration from tools like Cursor, Claude, VS Code, Windsurf, or a plain MCP server JSON file. It can also export configuration in the standard `mcpServers` JSON shape. + +That matters because MCP is an open ecosystem. The same server definition can often move between tools. ABP Studio becomes another place where I can use that server, but now in the context of an ABP-aware agent. + +## What A Connected Server Shows + +After a server is connected, ABP Studio shows the important parts of the connection. + +I can see whether the server is connected, how many tools it exposes, which tools are available, and whether it provides resources. I can also inspect resources directly from the settings page. + +![A connected SEO Analyzer MCP server and its available tools](seo-analyzer-mcp-tools.png) + +This visibility matters because an MCP server is not just a checkbox. It is a surface area. It may expose one tool or many. Some tools may be read-only, and some may perform actions outside the repository. + +The agent does not need me to call those tools manually. I describe the task, and if the tool is available and relevant, the agent can decide to use it. That is why a prompt can stay natural: + +```text +Read the product requirement from the connected knowledge base and compare it with the current implementation. +``` + +The prompt does not need to become a tool invocation script. The agent still owns the reasoning loop. MCP only gives it a new place to look. + +## Tool Access: What The Agent Is Allowed To Use + +The most important part of MCP in ABP Studio is not only connecting servers. It is controlling what the agent is allowed to use. + +Individual tools can be disabled. If a tool is disabled, it is not offered to the agent. The agent should not plan around it, call it, or assume it has access to it. + +That is useful for safety, but also for quality. + +![Disabling individual tools exposed by the SEO Analyzer MCP server](seo-analyzer-mcp-tools-disabled-indivually.png) + +Every enabled tool becomes part of the menu the agent considers. If a server exposes ten tools but the current task only needs two, I usually prefer to enable only those two. A shorter tool list is easier for the agent to choose from. It also makes the session easier for me to reason about. + +The permission model is simple: + +- If a server is not connected, the agent cannot use it. +- If a server is disabled, the agent cannot use its tools. +- If an individual tool is disabled, the agent cannot use that tool. +- If the session is not in Agent mode, MCP tools are not available. + +That last point is important. MCP tools are available in **Agent mode only**. + +Plan and Ask modes are read-only by design. They are useful for understanding, planning, and discussing changes, but they do not receive MCP tools. If I want the agent to call an external MCP tool, I need to work in Agent mode. + +This keeps the model consistent with the rest of the ABP AI Coding Agent experience. The mode determines what kind of work the agent is allowed to perform. + +## Keeping MCP Safe + +MCP is powerful because it lets an AI agent reach systems outside the solution. + +That is also why it needs care. + +An MCP server is a program. It may read files, call APIs, query databases, send requests, or perform actions depending on how it was built. The agent is not inventing those capabilities. It is using what the server exposes. + +So trust matters at two levels. + +First, I need to trust the actions. If a tool can update an issue, send a message, change a record, or trigger a workflow, that is a real side effect. + +Second, I need to trust the descriptions. When an MCP server connects, the names and descriptions of its tools become part of what the model can read. A careless or hostile server can use that text to influence the model before I even write my prompt. + +That means MCP safety is not only about "what can this tool do?" It is also about "what instructions or descriptions does this server place in front of the model?" + +Two habits keep this manageable: + +- Connect only servers I trust. +- Disable tools the agent should not use for the current task. + +This sits on top of the agent's normal guardrails. Shell commands, URL fetches, downloads, and other sensitive actions can still require permission. Also, the tool list is fixed for a running session, so changing a server in the middle of a session does not quietly alter the tool surface already given to that session. + +That is the behavior I want from this kind of integration. MCP can extend the agent, but it should do so through visible, intentional configuration. + +## A Practical MCP Walkthrough + +Let me make this more concrete with a small scenario. + +Imagine I am working on the public website or documentation side of an ABP-based product. The task is not a compiler error or a failing unit test. I want to understand how the website looks from an SEO perspective and what I would improve first. + +Without MCP, I would open a separate SEO tool, run the audit, wait for the result, copy the score, paste the table into the chat, and then ask the agent to interpret it. That is not terrible, but it turns me into a copy-paste integration layer. + +With an SEO Analyzer MCP server connected, I can start from a better prompt: + +```text +Please rate the SEO work on the https://abp.io website on a scale of 1 to 10. I'd also like to know what you would do if you were in charge. +``` + +At that point, the agent can use the SEO Analyzer MCP tool instead of guessing from general SEO knowledge alone. It can call the audit tool, read the structured result, and turn it into a concrete improvement plan. + +![ABP AI Coding Agent using the SEO Analyzer MCP server](using-mcp.png) + +The value is not only that the agent saved me a few seconds. The value is that the agent starts from live tool output instead of a manually summarized report. + +### First, Without The MCP Server + +If no SEO MCP server is connected, the agent cannot run the audit directly. + +It may still give a reasonable answer from general knowledge. It may say that the site should have good titles, descriptions, headings, performance, backlinks, structured data, and content targeting. That advice can be useful, but it is generic. + +For example, I might write: + +```text +Review the SEO of abp.io and tell me what to improve. +``` + +The agent can reason about common SEO practices, but it does not have a fresh audit result. It does not know which categories scored well, which areas are weak, or whether the live page data confirms the concern. + +This is similar to debugging without monitoring tools. The agent can reason, but it is reasoning from weaker evidence. + +### Then, With The MCP Server Enabled + +Now imagine the SEO Analyzer MCP server is connected, enabled, and the relevant audit tool is enabled. + +I can ask: + +```text +Run an audit on https://abp.io, rate the result, and suggest the highest-impact improvements. +``` + +This time the agent can gather the external context first. In the screenshot, the server exposes tools like `run_audit_anonymous`, `run_audit`, `get_audit`, `list_audits`, `get_audit_pdf`, and `get_organic_traffic`. The agent uses the audit tool and then explains the result in human terms. + +That changes the workflow: + +1. Run the external SEO audit. +2. Read the score and category breakdown. +3. Identify the weakest areas. +4. Suggest concrete technical or content improvements. +5. If needed, inspect the related website or documentation files. + +The agent is no longer guessing from best practices alone. It can connect the outside report to the actual website and then, if the relevant files are in the solution, help improve them. + +### Another Example: Live Monitoring + +SEO is only one example. Monitoring is another strong fit for MCP. + +Prometheus MCP servers can expose tools for PromQL instant queries, range queries, metric discovery, targets, and server information. That means I can ask operational questions in natural language while the agent queries the monitoring system through MCP. + +For example: + +```text +Use the Prometheus MCP server to check p95 latency, request rate, and error rate for the public web application after the last deployment. If something changed, identify the most likely area to inspect in the ABP solution. +``` + +That does not mean the agent should blindly change production behavior. It means the agent can start from live evidence: metrics, trends, targets, and recent changes. Then it can use ABP Studio context to inspect the related application, module, or configuration. + +### Combining MCP With ABP Studio Tools + +The best part is that MCP does not replace the ABP Studio tools from the previous article. It works beside them. + +In a real workflow, MCP may provide the outside signal, and ABP Studio tools may provide the inside development loop: + +1. MCP brings in the external context. +2. ABP-aware reasoning maps it to the solution. +3. ABP Studio tools help implement and validate the change. + +That is where the integration becomes more useful than a generic MCP checkbox. The agent is connected to an external tool while still understanding the ABP development environment. + +## How MCP Fits With Everything Else + +MCP is an open standard, so connecting an MCP server is not an ABP-only idea. + +Tools like Cursor, Claude, VS Code extensions, and other AI coding environments can also use MCP. That is part of its appeal. Teams can build or adopt a server once and use it across different tools. + +What makes MCP especially useful in ABP Studio is the company it keeps. ABP AI Coding Agent already understands the ABP solution structure, application layers, modules, migrations, proxies, build actions, run profiles, containers, monitoring data, and official ABP documentation. MCP adds the outside world to that picture. + +So I do not need to choose between: + +- an agent that understands ABP, +- and an agent that can reach my team's external systems. + +The better experience is both together, under visible configuration. Put simply: + +- Built-in ABP Studio tools handle the solution and runtime environment. +- MCP servers handle external tools and data sources. +- Rules, skills, and lessons shape how the agent behaves. +- Agent mode decides whether tool use is available for the session. + +Each part has a different role. Keeping those roles clear makes the agent easier to trust. This is also why I would start small in a real team workflow: one low-risk, high-value server, probably a read-only analytics, documentation, or monitoring server, with only the tools that support a clear workflow enabled. + +MCP by itself is a protocol. The difference in ABP Studio is that MCP becomes part of an ABP development session. The agent can read an external SEO report or monitoring signal, but it can also understand which ABP application, module, page, or configuration owns the behavior and validate the change with ABP Studio tools. + +Most real tasks are not only "call an external tool." They are more like: + +1. Understand the requirement from outside the repository. +2. Find where that behavior lives in the ABP solution. +3. Make the smallest correct change. +4. Validate it in the same development environment. + +MCP helps with the first part. ABP Studio AI helps connect the rest. + +**That makes MCP valuable not because it is another tool list, but because it extends the ABP AI Coding Agent beyond the repository without disconnecting it from the ABP workflow.** + +## Conclusion + +MCP is the agent's connection to everything that is not already inside the ABP solution. + +You will not need it for every task. But when the work depends on SEO data, live metrics, external documentation, internal services, or team knowledge, MCP gives the agent a standard way to reach that context. \ No newline at end of file diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools-disabled-indivually.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools-disabled-indivually.png new file mode 100644 index 0000000000..e1b631f99c Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools-disabled-indivually.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools.png new file mode 100644 index 0000000000..2160b389e0 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/seo-analyzer-mcp-tools.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/using-mcp.png b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/using-mcp.png new file mode 100644 index 0000000000..68810c6915 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-5-mcp/using-mcp.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/cover-image.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/cover-image.png new file mode 100644 index 0000000000..67e2819cac Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/cover-image.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-commit-message.gif b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-commit-message.gif new file mode 100644 index 0000000000..553e5b2b78 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-commit-message.gif differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review-details.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review-details.png new file mode 100644 index 0000000000..8e4c43718d Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review-details.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review.png new file mode 100644 index 0000000000..572ec3d4b7 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-ai-review.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-branch-and-stash.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-branch-and-stash.png new file mode 100644 index 0000000000..5f5d9078a9 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-branch-and-stash.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-changes-and-diff.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-changes-and-diff.png new file mode 100644 index 0000000000..dabd936b88 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-changes-and-diff.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-diff-comments.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-diff-comments.png new file mode 100644 index 0000000000..5ace3ff8eb Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-diff-comments.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-initialize-repository.png b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-initialize-repository.png new file mode 100644 index 0000000000..fcf2ad3929 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/git-initialize-repository.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/post.md new file mode 100644 index 0000000000..18036a1da3 --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-6-abp-studio-git-integration/post.md @@ -0,0 +1,212 @@ +# Deep Dive on ABP AI Agent #6: ABP Studio Git Integration + +When I use an AI coding agent, I do not only care about whether it can change files. + +I care about what happens around those changes. + +Which branch am I on? What exactly changed? Can I review the diff before I commit? Did I accidentally touch a file from another package? Is my branch behind the default branch? If a pull request already has feedback, can I bring that context back into the coding session without copying every comment by hand? + +That is where Git integration in **ABP Studio** becomes important. + +Git is not just the final step after the agent finishes. For me, it is the confidence layer around the whole workflow. It helps me keep AI-assisted work reviewable, recoverable, and connected to the same team process I already use every day. + +![ABP Studio Git Integration cover](cover-image.png) + +## Git In The AI Workflow + +Most development work is not a straight line from prompt to done. + +I may ask ABP Agent to make a small change, then review the diff and adjust the direction. I may ask it to address feedback from a pull request. I may start from a GitHub issue, create a branch, let the agent investigate, and then decide which changes are ready to commit. + +In all of those moments, Git gives me a practical boundary: + +* this is the branch I am working on, +* these are the files that changed, +* this is the diff I need to review, +* this is the commit I am about to create, +* and this is the context I want to send back to the team. + +Without a Git-aware workflow, AI changes can feel a little too loose. The agent may be productive, but I still need a clean way to inspect, group, commit, push, and discuss the result. + +ABP Studio Git Integration brings that loop into the same place where I already work with the solution and the agent. + +## Initializing Git For A Solution + +The Git panel starts with the active solution. + +If the solution is not a Git repository yet, ABP Studio does not pretend otherwise. It shows a simple empty state and lets me initialize Git from there. I can choose the initial branch name, create a `.gitignore`, and create the first commit. + +![Initializing Git for an ABP solution in ABP Studio](git-initialize-repository.png) + +That is useful for new ABP solutions because the first Git step is part of the project setup, not something I need to remember after the fact. + +If I want to put the solution on GitHub, Studio can also help with that path. After connecting my GitHub account, I can publish the repository under my account or an organization, choose the repository name, add a description, and decide whether it should be private. + +The small but important detail is that Git becomes part of the solution experience early. I do not need to move from ABP Studio to a separate Git tool just to create the repository before I start working with ABP Agent. + +## Changed Files And Diff Review + +Once Git is active, the Git panel becomes the place I check after an agent session or a manual edit. + +I can see the current branch, remote state, changed files, and the selected file diff. The changes are not only a flat list. In an ABP solution, they can be grouped in a way that follows the solution structure, so changes under different packages or solution areas are easier to scan. + +![Changed files and diff review in ABP Studio Git Integration](git-changes-and-diff.png) + +That grouping matters in real ABP work. + +If I asked ABP Agent to adjust a public web page, but I see changes in an admin package, I immediately know to slow down and review why. If a change touches a contract package and a UI package, the grouping helps me understand that relationship before I commit. + +The diff viewer is also part of the same loop. I do not need to leave the workspace just to answer the basic review question: + +```text +What did this task actually change? +``` + +That is the question I want to answer before a commit, especially when AI helped produce the diff. + +## Selected Files And Commit Messages + +Committing is not only pressing a button. + +I still want to choose which files belong together. I still want the commit message to match the change. I still want to avoid committing work on a protected branch by accident. + +ABP Studio keeps that flow visible. I can select the files I want, write a summary and description, and commit to the current branch. When AI is enabled, Studio can generate a commit message from the selected diffs. + +![Generating a commit message from selected changes](git-ai-commit-message.gif) + +I like this because it keeps the AI help close to the actual diff. + +A generic prompt like "write a commit message" depends on what I paste into the chat. In Studio, the commit message generator can work from the selected files. That makes the result more focused, and I still stay in control because the generated text lands in the commit fields before I use it. + +The protected branch warning is another important part of the experience. If the current branch should not receive direct commits, Studio makes that visible and pushes me toward the safer workflow: create a branch, review the diff, then commit there. + +That is the right kind of guardrail. It does not make Git complicated. It makes the normal team habit harder to miss. + +## Branching, Stashing, And Syncing + +AI-assisted development often starts with a branch decision. + +Sometimes I am starting fresh from the default branch. Sometimes I am building on work already in my current branch. Sometimes I have local changes and need to switch context without losing them. + +ABP Studio exposes those decisions in the Git panel. + +I can create a branch, switch branches, update from the default branch, fetch, pull, push, and see whether I am ahead or behind. If I switch branches while I have local changes, Studio asks what should happen to that work: leave it behind as a stash or bring it with me. + +![Branch switching and stashed changes in ABP Studio](git-branch-and-stash.png) + +That choice is more important than it looks. + +When I am working with an agent, I do not want local changes to silently follow me into the wrong branch. I also do not want to lose half-finished work just because I need to inspect another issue. The stash flow turns that into an explicit decision. + +The same idea applies to sync. + +If my branch is behind, I can update before I continue. If I have local commits, I can push them. If a merge or pull produces conflicts, Studio shows the conflicted files and gives me a path to resolve, abort, continue, or send the conflict context to ABP Agent. + +That keeps the Git workflow close to the coding workflow. I can move from change to review to sync without mentally switching tools. + +## AI Review And Manual Diff Comments + +There is a moment before a commit where I often want a second look. + +Not a full pull request review. Not a long architecture discussion. Just a focused pass over the files I selected: + +```text +Does this diff contain something suspicious? +Did the agent miss a small edge case? +Is there a line I should check again before committing? +``` + +ABP Studio supports that with AI review on selected Git changes. + +![AI review suggestions on selected Git changes](git-ai-review.png) + +![AI review suggestions on selected Git changes](git-ai-review-details.png) + +AI review is not the only way to leave notes on a diff. I can also write my own comments directly on changed lines while I am reviewing. + +![Manual comments on Git diff in ABP Studio](git-diff-comments.png) + +The useful part is that the review is attached to the diff. Suggestions and notes appear near the changed lines, and if there is something I want the agent to handle, I can send those review notes to ABP Agent. + +That changes the feel of the workflow. + +Instead of asking the agent to code and then manually re-explaining my review comments, I can turn the review result back into a task. Whether a note comes from AI review or from something I wrote myself, the agent gets the file, line, and note context. I still review the result, but I spend less time copying context between places. + +Git also helps with recovery. In a Git repository, ABP Studio can offer **Back to this point** in the agent conversation. For me, that is a comfort feature: if an agent turn takes the work in the wrong direction, I can return to an earlier point instead of manually untangling every changed file. + +I still treat Git commits as the real checkpoints for team work. But during a live agent session, being able to go back to a previous point makes experimentation feel less risky. + +## GitHub Issue Context + +Many tasks do not start as a prompt. They start as an issue. + +The issue has the requirement, comments, labels, screenshots, and sometimes a conversation about what is expected. If that context stays only in the browser, I have to copy it into the agent manually. + +ABP Studio can bring GitHub issues into the Git area. + +I can filter issues, open one, read the description and comments, create a branch for that issue, and send the issue context to ABP Agent. + + + +That makes the workflow feel natural: + +1. Pick the issue. +2. Create a branch for it. +3. Send the relevant context to ABP Agent. +4. Let the agent inspect the solution and implement the change. +5. Review the Git diff before committing. + +The important detail is that the agent starts from the same context I would start from as a developer. It sees the issue title, description, labels, included comments, and attached images when they are part of the selected context. + +That is much better than writing a vague prompt that tries to summarize the issue from memory. + +## Pull Request Feedback Context + +Pull request feedback is another place where Git integration helps the AI workflow. + +When I open a pull request inside ABP Studio, I can see the PR title, branches, comments, reviews, and requested changes. If I am not on the PR branch, Studio can switch to it. Then I can choose which comments or requested changes should be included and send that context to ABP Agent. + + +This is the workflow I want when a reviewer asks for changes: + +* read the feedback, +* switch to the right branch, +* include only the relevant comments, +* send the request to ABP Agent, +* review the resulting diff, +* commit and push. + +The include and exclude controls matter here. Not every PR comment should become an agent instruction. Some comments are discussion, some are already resolved, and some are optional. I want to choose what becomes context. + +That keeps the agent from treating the entire PR timeline as one undifferentiated command. I can shape the task before sending it. + +## Git Integration In The Deep Dive Series + +In the earlier articles, we looked at modes, tools, MCP, scopes, and workflows. + +Git integration connects those ideas to the normal development lifecycle. + +* **Ask and Plan** help me understand and shape the work. +* **Agent mode** can make the change. +* **Tools** help the agent use ABP Studio context. +* **Scopes** keep the working area focused. +* **Workflows** make repeated actions predictable. +* **Git integration** lets me review, recover, commit, push, and collaborate around the result. + +That last part is easy to underestimate. + +The value of an AI coding agent is not only how quickly it can modify files. The value is whether I can bring those modifications into a professional development workflow without losing control. + +Git is the structure that makes that possible. + +## Conclusion + +ABP Studio Git Integration makes ABP Agent feel more grounded. + +It gives me a clear path from issue to branch, from agent work to diff review, from selected files to commit, and from pull request feedback back into the agent. + +For everyday work, that means fewer context switches. For AI-assisted work, it means more confidence. + +I can let the agent help, but I do not have to accept the result blindly. I can inspect the diff, ask for a review, commit intentionally, push when ready, and keep the whole process connected to GitHub and the team workflow. + +That is the part I value most: **Git integration turns AI output into reviewable development work.** diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/ai-agent-panel.png b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/ai-agent-panel.png new file mode 100644 index 0000000000..c0fcd9a3e7 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/ai-agent-panel.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/auth-identity-scope.png b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/auth-identity-scope.png new file mode 100644 index 0000000000..1fb92ec9ca Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/auth-identity-scope.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/post.md new file mode 100644 index 0000000000..4b9aa4b8d7 --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/post.md @@ -0,0 +1,157 @@ +# Deep Dive on ABP AI Agent #7: Scopes + +When I use an AI coding agent in a real ABP solution, I do not always want it to see everything. + +That may sound strange at first. More context usually feels better. But in a large solution, more context can also mean more noise, more unrelated files, and more chances for the agent to drift into an area that is not part of the task. + +If I am working on the public side of a modular application, I do not want the agent to redesign the admin side. If I am changing a Catalog module, I do not want it to spend half the session reasoning about Identity, SaaS, or Payment code. If I am fixing one microservice, I do not want the agent to treat the whole platform as editable surface area. + +That is where **AI Scopes** become one of the most important control features in **ABP Studio AI Coding Agent**. + +## Why Scopes Matter? + +Most AI coding agents are very good at reading a folder and making changes. That is useful, but an ABP solution is rarely just a folder. + +An ABP solution can contain modules, packages, applications, gateways, background workers, database projects, shared contracts, UI projects, and infrastructure configuration. In a microservice solution, the repository may contain multiple independently meaningful services. In a modular monolith, a single solution may still have clear business boundaries. + +-> In those situations, the question is not only: **Can the agent understand the solution?** ❌ + +-> The better question is: **Which part of the solution should the agent be allowed to work with for this task?** ✅ + +AI Scopes answer that question directly. + +They let me choose the accessible area before the session starts. The agent can then focus on the relevant module, package, solution area, or external folder instead of treating the entire repository as equally relevant. + +For me, that changes the feeling of using an AI agent. It is no longer "here is my whole codebase, please be careful." It becomes "here is the part of the system this task belongs to, work inside that boundary." + +## What An AI Scope Controls? + +An AI Scope **restricts which directories the agent can access during a session**. + +![Auth and Identity scope configuration in ABP Studio](auth-identity-scope.png) + +Depending on the task, a scope can include: + +* the whole solution, +* selected modules, +* selected packages, +* selected external folders, +* or a focused combination of these. + +The important part is that this is not only a prompt suggestion. It is part of the session context and file access boundary. File paths used by the agent are validated against the resolved scope. If a file is outside the accessible directories, the agent should not treat it as part of the editable workspace. + +Scopes also work together with `.abpignore`. Even if a file is under an accessible directory, files excluded by `.abpignore` remain blocked. That gives teams two useful layers: + +* **Scopes** decide which solution areas are relevant to the task. +* **`.abpignore`** protects files that should stay inaccessible, such as secrets, certificates, environment files, or other sensitive local data. + +This is a practical control model. I can narrow the agent's working area without pretending that the repository is smaller than it really is. + +## Scope Is Locked To The Session + +Another detail I like is that scope belongs to the AI Agent session. + +The first message of a session locks the configuration that affects the system prompt, including the active AI Scope. If a background session continues running and I change the foreground scope later, that running session does not silently change its context. + +That matters when multiple sessions are active. + +![Selected AI scope shown in the agent session panel](selected-scope.png) + +Imagine I have one session working on a Catalog module and another session answering questions about the whole solution. Those sessions should not accidentally share a changing boundary. Each one should keep the scope it started with. + +This makes scopes more predictable. I can choose the scope intentionally at the beginning of the work and trust that the session is tied to that decision. + +## Focused Autonomy + +Scopes are not about making the agent weaker. They are about making autonomy more focused. + +When I narrow the scope, I am not saying the agent is less capable. I am saying the task has a boundary. + +For example: + +```text +Use the Public AI Scope. +Add a small validation improvement to the public product search flow. +Do not inspect or change the Admin side unless you find a direct contract dependency. +``` + +That kind of prompt becomes much stronger when the selected scope already matches the instruction. The agent receives both the natural-language task and the platform-level boundary. + +This is especially useful for ABP because ABP applications are built around clear concepts: modules, layers, packages, application services, repositories, DTOs, permissions, localization resources, DbContexts, and run profiles. A scope can follow those boundaries instead of relying only on a long prompt. + +## What Scopes Help Prevent? + +Scopes help reduce a few common AI-agent failure modes. + +- First, they reduce **unrelated exploration**. The agent does not need to spend time discovering files that have nothing to do with the task. +- Second, they reduce **accidental edits**. When a task belongs to one module, the agent should not casually change another module just because it found a similar type there. +- Third, they improve **reviewability**. If I scoped the task to `Catalog`, and the diff changes `Identity`, that is immediately suspicious. The boundary makes the review easier. +- Fourth, they support **parallel work**. Different sessions can be scoped to different areas, which is useful when independent tasks are running in the same solution. + +This is one of the places where ABP AI Coding Agent feels different from a generic coding tool. The feature is not only "the model can read fewer files." It is integrated into ABP Studio's understanding of the solution. + +## Scopes And ABP Solution Architecture + +ABP already encourages clear boundaries. + +In a layered module, the Domain layer should not depend on the Application layer. HTTP API projects should depend on contracts, not implementation projects. Entity Framework Core and MongoDB integrations should stay behind the domain abstractions. A reusable module should be understandable as a module, not only as a set of files. + +AI Scopes fit naturally into that mindset. + +If I am working on a Domain change, I can keep the scope close to the module and its required shared contracts. If I am working on UI behavior, I can include the UI package and the related contract package. If I am working on a microservice, I can scope the agent to that service and only add external folders when they are truly required. + +That means the agent's working area can follow the same mental model I already use as an ABP developer: + +```text +What bounded area owns this change? +Which packages are needed to make it safely? +Which parts of the system should stay out of this session? +``` + +## Scopes And Workflows Work Better Together + +- Scopes define **where** the agent can work. +- Workflows define **what deterministic actions** should happen around that work. + +![ABP AI Coding Agent panel showing scopes and workflows combined](scopes-openning.png) + +That combination is powerful. For example, I can scope the agent to the `Catalog` module and use a workflow that builds the affected package, regenerates proxies if contracts changed, and restarts the related application. + +The scope keeps the coding session focused. The workflow keeps the verification loop repeatable. + +This is the larger ABP Studio AI story. It is not only an AI chat window. It is an agent inside a platform that already understands ABP solutions, run profiles, tools, workflows, Git state, and runtime signals. + +## Why This Is Different From Generic Coding Agents? + +Tools like Cursor, Claude Code, Codex, and Windsurf are strong general-purpose coding tools. They can read files, edit code, run shell commands, and help with many projects. + +**ABP AI Coding Agent is different because it is built around ABP Studio's view of an ABP solution.** + +Scopes are a good example of that difference. + +In a generic tool, I can try to simulate scope with a prompt: + +```text +Only work in this folder. +``` + +That is helpful, but it is still mostly an instruction. In ABP Studio, scope is part of the agent session and file access model. It can be selected intentionally before the work starts, stored with the session, and combined with `.abpignore`, workflows, tools, plans, and run profile context. + +For professional ABP teams, that matters. The goal is not to give an AI agent unlimited access and hope the prompt is clear enough. The goal is to create a controlled development loop where the agent understands the system, works in the right area, uses the right tools, and produces a diff that is easier to trust. + +## Conclusion + +AI Scopes make ABP Studio AI Coding Agent feel more deliberate. + +They let me say: + +* this is the part of the solution that matters, +* this is the boundary for the current session, +* this is the context the agent should focus on, +* and everything else should stay outside unless we intentionally expand the scope. + +That is exactly the kind of control I want when using AI in real ABP solutions. + +The agent can still be powerful. It can still plan, edit, build, run tools, and iterate. But with scopes, that power is pointed at the right part of the system. + +That is the real value: **not just more AI autonomy, but better-shaped AI autonomy for ABP development.** diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/scopes-openning.png b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/scopes-openning.png new file mode 100644 index 0000000000..6297b817e8 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/scopes-openning.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/selected-scope.png b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/selected-scope.png new file mode 100644 index 0000000000..b9039e28c2 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/selected-scope.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/cover-image.png b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/cover-image.png new file mode 100644 index 0000000000..4ba1f3f4c8 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/cover-image.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/model-settings.png b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/model-settings.png new file mode 100644 index 0000000000..1f130d4888 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/model-settings.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/post.md new file mode 100644 index 0000000000..67f9279082 --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-8-parallel-agent-execution/post.md @@ -0,0 +1,90 @@ +# Deep Dive on ABP AI Agent #8: Parallel Agent Execution + +Real work rarely lines up one task at a time. + +While the agent is busy adding a feature, a question comes up about a different module. A review is waiting. A small fix would take two minutes, but the agent is in the middle of something else. With a single session, you wait. You watch one task finish before you can start the next, even when the two have nothing to do with each other. + +ABP Studio does not make you wait. It can run several agent sessions at the same time, each on its own task. + +## Parallel Agent Sessions + +A session is one conversation with the agent. It has its own history, its own mode, its own model, its own scope, and its own workflow. ABP Studio keeps multiple sessions per solution, and they can run in parallel. + +There is a limit, and it is there on purpose. By default you can run up to **3 sessions at once**, and you can set that anywhere from **1 to 5**. When you send more prompts than there are open slots, the extra ones are queued and start as soon as a slot frees up. + +So "parallel" here is not a trick of switching back and forth quickly. The sessions actually run at the same time, up to the limit you choose. + +## Parallel Session Use Cases + +The point is to stop letting one task block another. A few ways it plays out: + +* One session implements a feature while another answers questions about a different part of the solution. +* Two unrelated modules get worked on at once, each in its own session. +* One session writes the code while another reviews a diff or does research. + +Because each session is separate, you can even point them at different parts of the solution and give them different jobs: + +```text +Session 1 (Agent): Add a Category screen to the Catalog module. +Session 2 (Ask): Explain how permissions flow from the API to the UI. +``` + +The first one edits files. The second one only reads and answers. They do not interfere, because they are different sessions with different settings. + +## Per-Session Settings Isolation + +This is the part that makes parallel work predictable instead of chaotic. + +When a session sends its first message, two things happen at different levels. The session permanently locks its **scope** and **workflow**—these stay fixed for the entire session lifetime and cannot be changed once the first message is sent. On the other hand, the **model**, **mode**, and **active tools** are snapshotted fresh at each run (each turn the agent takes), so they reflect whatever is configured at the moment the run starts but remain stable for its duration. + +That matters the moment you have more than one session open. Say one session is running in the background, scoped to the `Catalog` module. You switch the foreground to a different scope to start a second task. The background session does not notice. It keeps the scope and workflow it was locked to, and each of its runs uses whatever model and tools were configured at the moment that run began. + +Without this, parallel sessions would quietly corrupt each other every time you changed a setting. With it, each session is a sealed unit of work. + +## The Prompt Queue + +You do not have to wait for a session to be idle to line up its next step. + +While a session is running, you can queue more prompts on it. They attach to the same session and are sent one after another, each after the current turn finishes. The queue keeps the session's settings, so a queued prompt runs with the same mode, scope, model, workflow, and active plan as the rest of that session. + +This works together with the concurrency limit. Prompts that cannot start right away, because every slot is busy, simply wait their turn instead of failing. + +## Keeping Parallel Work Safe + +Running several sessions at once is powerful, and it also gives you a new way to get in your own way: two sessions editing the same files. + +ABP Studio helps here, but it does not pretend the problem away. It tracks file changes, so if one session edited a file after another session read it, the second is told to read it again before overwriting. It also serializes operations that cannot safely overlap, such as adding a migration while a build is running, so two sessions do not run conflicting `dotnet` commands at the same time. And because each session tracks its own pending questions, one session waiting on your input does not freeze the others. + +Still, the simplest rule is the best one: keep parallel Agent-mode sessions on separate parts of the solution. This is exactly where scopes earn their place. Give each session its own scope, and they stay in their own lanes by design instead of by luck. + +## A Second Kind Of Parallel: Subagents + +There is also a smaller, quieter form of parallel work that happens inside a single session. + +When the agent needs to look something up, it can fan out multiple **subagents** in a single turn: read-only helpers that research in parallel and return their results before the main agent continues. There are a few kinds, each with a narrow job: + +* one that researches your solution and code, +* one that searches the web, +* one that searches and reads the official ABP documentation. + +These research-style subagents (code research, web search, documentation search) are read-only—they cannot modify files or solution state. They only read, gather, and hand back a short summary. That keeps the main session's context clean, because the digging happens elsewhere and only the answer comes back. (Note that the browser subagent is an exception: it is stateful and can mutate the shared browser session.) + +ABP Studio can use a separate model for research subagents, configured apart from your main one. A fast, cheap model is the right choice for this kind of lookup work. + +![Model Settings: the research model used by subagents is set separately from the main model](model-settings.png) + +So there are two scales of parallel here. Sessions run independent tasks side by side. Subagents run read-only research inside one task. Both keep the slow parts from blocking the useful parts. + +## ABP Studio Parallel Execution Model + +Plenty of tools let you open more than one chat, and that is genuinely useful. So the idea of running agents in parallel is not, by itself, an ABP feature. + +The difference is that each session here carries the context of an ABP solution and stays inside it. A session locks its scope and workflow permanently, and snapshots its model and tools per run, so background work does not drift when you change the foreground. Studio coordinates the operations that would otherwise collide, like builds and migrations, across all the running sessions. And scopes give each session a clear boundary, so parallel does not turn into a pile of agents editing the same files. + +In short, the parallelism is not just several chat windows. It is several controlled, solution-aware sessions that know how to stay out of each other's way. + +## Conclusion + +Parallel execution is about respecting how work actually arrives: more than one thing at a time, often unrelated. + +ABP Studio lets you run several agent sessions together, each sealed to its own settings, with a queue for what comes next and guardrails for the places where parallel work could collide. Inside each session, subagents add a second layer of parallel research without cluttering the main task. diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/ai-agent-panel.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/ai-agent-panel.png new file mode 100644 index 0000000000..c0fcd9a3e7 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/ai-agent-panel.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md new file mode 100644 index 0000000000..49c4f51e60 --- /dev/null +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md @@ -0,0 +1,205 @@ +# Deep Dive on ABP AI Agent #9: Workflows + +There is a pattern I see in almost every real development session. + +The interesting part is the code change, but the repeated part is everything around it: + +* start the containers, +* build the affected packages, +* add a migration if the model changed, +* regenerate proxies if the API contract changed, +* restart the application, +* run the validation task, +* check the logs when something fails... + +When I work alone, I can do those steps manually. When I work with an AI coding agent, I do not want to keep pasting the same checklist into every prompt. I want the tool to understand that this solution has a normal way of preparing, validating, and recovering after changes. + +That is the point of **ABP Studio AI Agent Workflows**. + +![ABP Studio AI Agent Workflows overview](workflow.jpg) + +Workflows let me define repeatable actions around an agent run. The model can focus on the ambiguous part, understanding the requirement and changing the code, while ABP Studio handles the deterministic parts that should happen before or after the work. + +That combination is one of the clearest differences between **ABP AI Coding Agent** and a generic coding assistant. It is not only an editor with a chat panel. It is an agent inside a platform that **already knows how to build, run, migrate, generate proxies, manage containers, execute tasks, and inspect runtime signals.** + +## Why Workflows Matter? + +LLMs are powerful because they can reason through unclear requirements and modify code across files. But many development steps should not be creative. + +If the team always builds a package after an application service change, that should be predictable. If API contract changes require proxy generation, that should not depend on whether I remembered to mention it. If a local run needs containers before the application starts, that setup should not be reinvented in every prompt. + +Workflows give ABP Studio a place to encode those repeatable steps. + +![Opening the workflow settings panel in ABP Studio](workflows-openning.png) + +![ABP AI Agent workflow settings](workflow-settings.png) + +For me, the value is not only automation. It is also consistency. + +Instead of asking: + +```text +Please implement this, and then build, and maybe regenerate proxies, +and restart the app, and also add a migration if needed. +``` + +I can configure the workflow once and let the agent session carry that context. + +![Sample ABP AI Agent workflow configuration](sample-workflow-1.png) + +That makes the prompt cleaner: + +```text +Add the missing status filter to the order list. +Use the selected workflow for validation after the change. +``` + +The workflow becomes part of the development environment, not a long instruction I repeat manually. + +## Before And After The Agent Works + +An AI Agent workflow has two sides: + +* **Before steps** prepare the environment before the agent starts coding. +* **After steps** guide the validation and follow-up work after the main task is complete. + +Before steps run automatically in **Agent** mode. They are useful for setup actions that should happen before the model receives control, such as starting containers or running a preparation task. + +Plan and Ask modes are read-only, so they do not execute before steps. That separation is important. If I am only asking a question or asking for a plan, I do not want Studio to start applications or run mutation-oriented tooling. + +After steps are injected into the agent instructions as post-task guidance. The agent is expected to run the relevant post-steps after completing the work, but it can skip actions that do not apply. + +For example, if my workflow includes "Add Migration" but the change does not touch the EF Core model, the agent should not create an empty migration just because the workflow exists. The workflow gives deterministic options, but the agent still uses the actual change to decide what is relevant. + +## What A Workflow Can Do? + +Workflow actions are built around the things ABP developers already do in ABP Studio. + +![ABP AI Agent workflow actions](workflow-actions.png) + +The supported actions include: + +* **Build** the solution, selected modules, selected packages, or configured targets. +* **Start Application** for selected applications, folders, or all runnable applications. +* **Stop Application** when validation needs a clean state. +* **Restart Application** after code changes. +* **Start Containers** for databases, caches, message brokers, or other dependencies. +* **Stop Containers** when the workflow needs to clean up. +* **Run Task** for configured ABP Studio tasks. +* **Add Migration** when entity changes require a database migration. +* **Generate C# Proxies** after contract changes. +* **Generate Angular Proxies** after API changes consumed by Angular clients. + +That list is very ABP-specific. + +A generic coding tool can run shell commands, and that is useful. But ABP Studio workflows know about ABP Studio concepts: applications, containers, run profile tasks, packages, modules, migrations, and proxy generation. The agent can use those as first-class actions instead of trying to infer everything from terminal commands. + +## Personal And Shared Workflows + +Workflows can be personal or shared. + +![Sharing an ABP AI Agent workflow with the team via run profile](shared-with-team.png) + +- A **personal workflow** is stored locally under the solution workspace. It is useful for my own development habits. Maybe I like restarting a specific app after each agent turn. Maybe I have a local task that only makes sense on my machine. +- A **shared workflow** is stored with the active run profile. That makes it suitable for source control and team usage. + +This is where workflows become more than a convenience feature. A team can encode its normal AI-agent validation path into the solution itself. + +For example: + +```text +Before: +- Start required containers +- Run the prepare-local-environment task + +After: +- Build affected packages +- Generate Angular proxies when contracts changed +- Restart the Web and API applications +- Run the smoke-test task +``` + +Every developer using that run profile can work with the same repeatable loop. The workflow does not replace code review or testing, but it raises the baseline for what happens after the agent touches code. + +## Workflows Make AI More Deterministic Where It Should Be + +- I do not want the model to creatively decide whether my team usually runs proxy generation. I want the workflow to encode that. +- I do not want every prompt to include a long validation checklist. I want the workflow to carry it. +- I do not want an agent to guess which applications belong to the local run. I want ABP Studio's run profile to provide that context. + +This is why workflows are such a strong fit for ABP Studio AI Coding Agent. The model stays flexible where flexibility helps, and the platform stays deterministic where determinism matters. + +The result is a cleaner division of responsibility: + +| Responsibility | Best handled by | +| --- | --- | +| Understanding the requirement | AI Agent | +| Finding and editing relevant code | AI Agent | +| Starting known containers | Workflow | +| Running known tasks | Workflow | +| Adding migrations when needed | Agent using workflow action | +| Generating proxies when contracts change | Agent using workflow action | +| Building affected packages | Workflow / Studio tools | +| Investigating runtime failures | Agent using monitoring tools | + +## Workflows And Scopes Together + +Workflows are even better when combined with AI Scopes. + +Scopes define where the agent can work. Workflows define what repeatable actions should happen around that work. + +For example, I can select a `Catalog` scope and a workflow that: + +* builds the `Catalog` package, +* regenerates proxies if contracts changed, +* restarts the public web app, +* checks recent exceptions after restart. + +That is a focused agent loop. The agent does not need the whole repository, and the validation path does not need to be invented from scratch. + +This is the kind of full flow that makes ABP AI Coding Agent feel different from tools that only operate at the file-and-terminal level. + +## Why This Is Different From Generic Coding Agents + +Generic coding agents can be excellent: Cursor, Claude Code, Codex, Windsurf, and similar tools can read code, edit files, run shell commands, and help across many kinds of projects. + +But ABP Studio AI Coding Agent is built for a different experience: **It works inside ABP Studio, where the solution already has run profiles, applications, containers, tasks, modules, packages, migrations, proxy generation, monitoring, Git integration, and ABP-aware analysis.** + +Workflows use that platform context. + +Instead of saying: + +```text +Run whatever commands seem appropriate. +``` + +I can say: + +```text +Use the selected ABP Studio workflow. +``` + +That is a very different contract. The workflow is _visible, configurable, repeatable, and tied to the solution_. + +For ABP teams, this matters because the development process is not only code generation. It is code generation plus build, migration, proxy generation, application restart, runtime observation, and review. + +ABP Studio AI Coding Agent is designed for that full loop. + +## Conclusion + +Workflows make ABP Studio AI Coding Agent more practical for real development. + +They let me move repeated setup and validation steps out of my prompt and into the platform: + +* start what needs to be running, +* build what needs to be built, +* generate what needs to be regenerated, +* migrate when a model change requires it, +* restart the relevant apps, +* and continue the debugging loop with runtime evidence. + +That is why I see workflows as one of the features that makes ABP AI Coding Agent feel complete. + +The agent is not isolated from the development environment. It works inside ABP Studio, with the same solution structure, run profile, tools, and team workflow that I already use. + +That is the difference: **not just AI-generated code, but an AI-assisted ABP development flow from change to validation.** diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/sample-workflow-1.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/sample-workflow-1.png new file mode 100644 index 0000000000..b834ee979b Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/sample-workflow-1.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/shared-with-team.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/shared-with-team.png new file mode 100644 index 0000000000..08257f286b Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/shared-with-team.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-actions.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-actions.png new file mode 100644 index 0000000000..08d906d188 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-actions.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-settings.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-settings.png new file mode 100644 index 0000000000..e539c3cf65 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-settings.png differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow.jpg b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow.jpg new file mode 100644 index 0000000000..f7b8e5774b Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow.jpg differ diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflows-openning.png b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflows-openning.png new file mode 100644 index 0000000000..6297b817e8 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflows-openning.png differ diff --git a/docs/en/cli/index.md b/docs/en/cli/index.md index c02391eb42..8196ba993e 100644 --- a/docs/en/cli/index.md +++ b/docs/en/cli/index.md @@ -45,6 +45,7 @@ Here is the list of all available commands before explaining their details: - [new-package](../cli#new-package): Generates a new package based on the given template. - [update](../cli#update): Automatically updates all ABP related NuGet and NPM packages in a solution. - [clean](../cli#clean): Deletes all `BIN` and `OBJ` folders in the current folder. +- [clean-logs](../cli#clean-logs): Delete all `*logs.txt` files in the current folder and its subfolders. - [add-package](../cli#add-package): Adds an ABP package to a project. - [add-package-ref](../cli#add-package-ref): Adds package to given project. - [install-module](../cli#install-module): Adds a [multi-package application module](../modules/index.md) to a given module. @@ -476,6 +477,16 @@ Usage: abp clean ``` +### clean-logs + +Delete all `*logs.txt` files in the current folder and its subfolders. + +Usage: + +```bash +abp clean-logs +``` + ### add-package Adds an ABP package to a project by, diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index d9e3fee2f8..bbb1e53eaa 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -1966,6 +1966,22 @@ "path": "framework/ui/react-native", "isIndex": true }, + { + "text": "Running on Web", + "path": "framework/ui/react-native/running-on-web.md" + }, + { + "text": "Running on Device", + "path": "framework/ui/react-native/running-on-device.md" + }, + { + "text": "Manual Backend Configuration", + "path": "framework/ui/react-native/manual-backend-configuration.md" + }, + { + "text": "Android Emulator Setup", + "path": "framework/ui/react-native/setting-up-android-emulator.md" + }, { "text": "Styling with NativeWind", "path": "framework/ui/react-native/styling-with-nativewind.md" diff --git a/docs/en/framework/ui/react-native/index.md b/docs/en/framework/ui/react-native/index.md index c0957d5426..def2ac125c 100644 --- a/docs/en/framework/ui/react-native/index.md +++ b/docs/en/framework/ui/react-native/index.md @@ -1,25 +1,30 @@ ```json //[doc-seo] { - "Description": "Learn how to set up your development environment for React Native with ABP Framework, enabling seamless mobile app integration!" + "Description": "Learn how to set up your development environment for React Native with ABP Framework, enabling seamless mobile app integration!" } ``` ```json -//[doc-params] +//[doc-nav] { - "Architecture": ["Monolith", "Tiered", "Microservice"] + "Next": { + "Name": "Running on Web", + "Path": "framework/ui/react-native/running-on-web" + } } ``` # Getting Started with React Native -> The React Native mobile option is *available for* ***Team*** *or higher licenses* +> The React Native mobile option is _available for_ **_Team_** _or higher licenses_ The ABP platform provides a basic [React Native](https://reactnative.dev/) startup template to develop mobile applications **integrated with your ABP-based backends**. > The startup template UI is built with **[NativeWind v4](https://www.nativewind.dev/)** (Tailwind CSS for React Native) on top of a shadcn-inspired neutral palette, with full **light/dark mode** support. See [Styling with NativeWind](styling-with-nativewind.md) for the styling system reference. +React Native startup template running on a mobile emulator + ## How to Prepare Development Environment Please follow the steps below to prepare your development environment for React Native. @@ -27,10 +32,8 @@ Please follow the steps below to prepare your development environment for React 1. **Install Node.js:** Visit the [Node.js downloads page](https://nodejs.org/en/download/) and download the appropriate Node.js v20.11+ installer for your operating system. Alternatively, you can install [NVM](https://github.com/nvm-sh/nvm) to manage multiple versions of Node.js on your system. 2. **[Optional] Install Yarn:** You can install Yarn v1 (not v2) by following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 provides a better developer experience compared to npm v6 and below. You can skip this step and use npm, which is built into Node.js. 3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE that works seamlessly with TypeScript. While you can use any IDE, including Visual Studio or Rider, VS Code typically provides the best developer experience for React Native projects. -4. **[Optional] Install an Emulator/Simulator:** If you want to test on Android emulators or iOS simulators (instead of using the Web View method), you'll need to install one of the following: - - **Android Studio & Emulator:** Install [Android Studio](https://developer.android.com/studio) and set up an Android Virtual Device (AVD) through the AVD Manager. You can follow the [Android Studio Emulator guide](https://docs.expo.dev/workflow/android-studio-emulator/) on expo.io documentation. - - **Xcode & iOS Simulator:** On macOS, install [Xcode](https://developer.apple.com/xcode/) from the App Store, which includes the iOS Simulator. You can follow the [iOS Simulator guide](https://docs.expo.dev/workflow/ios-simulator/) on expo.io documentation. - > **Note:** The Web View method (recommended for quick testing) doesn't require an emulator or simulator. If you prefer a CLI-based approach for Android, you can check the [setting up android emulator without android studio](setting-up-android-emulator.md) guide as an alternative. + +Additional tools depend on how you plan to run the app — see the [Run the application](#run-the-application) section below. ## How to Start a New React Native Project @@ -38,9 +41,21 @@ You have multiple options to initiate a new React Native project that works with ### 1. Using ABP Studio -ABP Studio is the most convenient and flexible way to create a React Native application based on the ABP framework. Follow the [tool documentation](../../../studio) and select the option below: +ABP Studio is the most convenient and flexible way to create a React Native application based on the ABP framework. Follow the [tool documentation](../../../studio) and select the mobile option in the solution wizard: + +
+ +
+ABP Studio modern template — Mobile Application on Application Type step +

Modern template — on the Application Type step, enable Mobile Application (React Native with Expo).

+
+ +
+ABP Studio classic template — React Native on Mobile Framework step +

Classic template — on the Mobile Framework step, select React Native.

+
-React Native option +
### 2. Using ABP CLI @@ -54,106 +69,48 @@ abp new MyCompanyName.MyProjectName -csf -u -m react-native This command creates a solution containing an **Angular** or **MVC** project (depending on your choice), a **.NET Core** project, and a **React Native** project. -## Running the React Native Application - -> **Recommended:** For faster development and testing, we recommend using the **Web View** option first, as it requires fewer backend modifications. The backend configuration described in the next section is only needed if you want to test on Android emulators or iOS simulators. - -Before running the React Native application, install the dependencies by running `yarn install` or `npm install` in the `react-native` directory. - -### Web View (Recommended - Quickest Method) - -The quickest way to test the application is by using the web view. While testing on a physical device is also supported, we recommend using [local HTTPS development](https://docs.expo.dev/guides/local-https-development/) as it requires fewer backend modifications. - -Follow these steps to set up the web view: - -1. Navigate to the `react-native` directory and start the application by running: - ```bash - yarn web - ``` -2. Generate SSL certificates by running the following command in a separate directory: - ```bash - mkcert localhost - ``` -3. Set up the local proxy by running: - ```bash - yarn create:local-proxy - ``` - The default port is `443`. To use a different port, specify the `SOURCE_PORT` environment variable: -4. If you changed the port in the previous step, update the `apiUrl` in `Environment.ts` accordingly. -5. Update the mobile application settings in the database and re-run the migrations. If you specified a custom port, ensure the port is updated in the configuration as well: - ```json - "OpenIddict": { - "Applications": { - "MyApplication_Mobile": { - "ClientId": "MyApplication_Mobile", - "RootUrl": "https://localhost" - } - } - } - ``` - -### Running on Emulator/Simulator - -If you prefer to test on an Android emulator or iOS simulator, you'll need to configure the backend as described in the section below. Follow these steps: - -1. Make sure the [database migration is complete](../../../get-started?UI=NG&DB=EF&Tiered=No#create-the-database) and the [API is up and running](../../../get-started?UI=NG&DB=EF&Tiered=No#run-the-application). -2. Open `react-native` folder and run `yarn install` or `npm install` if you have not already. -3. Open the `Environment.ts` file in the `react-native` folder and replace the `localhost` address in the `apiUrl` and `issuer` properties with your local IP address as shown below: - -{{ if Architecture == "Monolith" }} - -react native monolith environment local IP - -{{ else if Architecture == "Tiered" }} - -react native tiered environment local IP - -> Make sure that `issuer` matches the running address of the `.AuthServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project. - -{{ else }} - -react native microservice environment local IP +## Run the Application -> Make sure that `issuer` matches the running address of the `.AuthServer` project, `apiUrl` matches the running address of the `.AuthServer` project. +You can choose how you want to run the mobile app: -{{ end }} +| Goal | Documentation | +| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Browser testing (fastest)** | [Running on Web](./running-on-web.md) — ABP Studio **Default** profile or Expo Web + HTTPS proxy at `https://localhost:8443` | +| **Emulator, simulator, or physical device** | [Running on Device](./running-on-device.md) — **Pro, non-tiered Monolith:** **MobileEmulator** profile or `yarn tunnel:api`; **Tiered / Microservice:** manual backend setup | -1. Run `yarn start` or `npm start`. Wait for the Expo CLI to print the options. +> **Before device testing (Monolith):** Install **cloudflared** using Cloudflare's [Download and install cloudflared](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) guide. The template's Quick Tunnel workflow does not require a Cloudflare account or the remaining steps in that document. -> The React Native application was generated with [Expo](https://expo.io/). Expo is a set of tools built around React Native to help you quickly start an app, and it includes many features. +Before running, you may need to install dependencies in the React Native project folder: -expo-cli-options +- **Monolith / Tiered / app-nolayers:** `react-native/` +- **Microservice:** `apps/mobile/react-native/` -In the image above, you can start the application on an Android emulator, an iOS simulator, or a physical phone by scanning the QR code with the [Expo Client](https://expo.io/tools#client) or by choosing the corresponding option. +Run `yarn install` or `npm install` in that folder. -### Expo +> **Recommended:** We suggest starting with [Running on Web](./running-on-web.md). Because it requires the fewest setup steps and provides faster development and hot-reload options compared to the physical device tests. -Press **i** to open the iOS simulator, or scan the QR code from the Expo CLI with your phone to run on a physical device. +### Related guides -### Android Studio +- [Manual Backend Configuration](./manual-backend-configuration.md): This includes a fallback HTTP/local IP setup for Tiered or Microservice architectures (or when Cloudflare tunnels are unavailable). +- [Setting Up Android Emulator Without Android Studio](./setting-up-android-emulator.md): This covers the CLI-based Android emulator setup based on your preference. -1. Start the emulator in **Android Studio** before running the `yarn start` or `npm start` command. -2. Press **a** to open in Android Studio. +The default login credentials, if not changed, are: -React Native login screen - -Enter **admin** as the username and **1q2w3E** as the password to log in to the application. - -The application is up and running. You can continue to develop your application based on this startup template. +- User name: **admin** +- Password: **1q2w3E\*** ## Navigation The startup template ships with **two navigation styles**, switchable when the project is created: -- **Bottom Tab** — *the default* — three tabs at the bottom of the screen: **Home**, **Settings** and **Account**. +- **Bottom Tab** — _the default_ — three tabs at the bottom of the screen: **Home**, **Settings** and **Account**. - **Drawer** — a side menu (hamburger) with two items: **Home** and **Settings**. Bottom Tab vs Drawer navigation comparison - Every main tab or drawer item is wired to **its own** native stack (`@react-navigation/native-stack`). Pushing more screens stays on that branch: the Back stack belongs to that tab or drawer route and does not mix with others. Bottom Tab and Drawer use the **same screen components**; they differ in how those screens are grouped and opened from the outer shell (and where the sign‑in/sign‑up flow lives in Bottom Tab versus Drawer). -> **How to choose:** The mode is selected in **ABP Studio** during the *Mobile Framework* step. Switching modes after the project is generated is not a one-line change — you would need to add the missing navigator (and its `@react-navigation/drawer` or `@react-navigation/bottom-tabs` dependency) manually, then update `src/AppContainer.tsx` and `src/navigators/types.ts` to match. Pick the mode upfront when possible. +> **How to choose:** The mode is selected in **ABP Studio** during the _Mobile Framework_ step. Switching modes after the project is generated is not a one-line change — you would need to add the missing navigator (and its `@react-navigation/drawer` or `@react-navigation/bottom-tabs` dependency) manually, then update `src/AppContainer.tsx` and `src/navigators/types.ts` to match. Pick the mode upfront when possible. ### Bottom Tab Navigation (default) @@ -161,7 +118,7 @@ The root navigator is `BottomTabNavigator` (`src/navigators/BottomTabNavigator.t - **HomeTab** → `HomeNavigator` → `HomeScreen` (hero greeting + feature cards). - **SettingsTab** → `SettingsNavigator` → `SettingsScreen` (language, theme, profile/password shortcuts). -- **AccountTab** → `AccountNavigator` — *conditional stack* based on the authentication state read from Redux: +- **AccountTab** → `AccountNavigator` — _conditional stack_ based on the authentication state read from Redux: - **Authenticated:** `AccountScreen` → `ChangePasswordScreen`, `ProfilePictureScreen`. - **Guest:** `LoginScreen` → `RegisterScreen`, `ForgotPasswordScreen`, `ResetPasswordScreen`. @@ -205,213 +162,6 @@ To add a screen to either navigation mode: 2. Register it as a `Stack.Screen` inside the appropriate navigator (e.g. `HomeNavigator`, `SettingsNavigator`, or `AccountNavigator`). 3. Add the route to the matching `*ParamList` in `src/navigators/types.ts` so the screen props stay typed. -If the new screen needs to appear at the *root* level (a new tab or drawer item rather than a child of an existing stack), edit `BottomTabNavigator.tsx` or `DrawerNavigator.tsx` and update the corresponding `BottomTabParamList` / `RootDrawerParamList` type. - -## How to Configure & Run the Backend (Required for Emulator/Simulator Testing) - -> React Native application does not trust the auto-generated .NET HTTPS certificate. You should use **HTTP** during the development. - -To disable the HTTPS-only settings of OpenIddict, open the {{ if Architecture == "Monolith" }}`MyProjectNameHttpApiHostModule`{{ else if Architecture == "Tiered" }}`MyProjectNameAuthServerModule`{{ end }} project and add the following code block to the `PreConfigureServices` method: - -```csharp -#if DEBUG - PreConfigure(options => - { - options.UseAspNetCore() - .DisableTransportSecurityRequirement(); - }); -#endif -``` - -> **Important:** Before running the backend application, make sure you have completed the [database migration](../../../get-started?UI=NG&DB=EF&Tiered=No#create-the-database) if you are starting with a fresh database. The backend application requires the database to be properly initialized. - -A React Native application running on an Android emulator or a physical phone **cannot connect to the backend** on `localhost`. To resolve this, you need to run the backend application using the `Kestrel` configuration. - -{{ if Architecture == "Monolith" }} - -React Native monolith host project configuration - -- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address in the `RootUrl` property with your local IP address. Then, run the database migrator. -- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. - ```json - { - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:44323" //replace with your host port - } - } - } - } - ``` - -{{ else if Architecture == "Tiered" }} - -React Native tiered project configuration - -- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address in the `RootUrl` property with your local IP address. Then, run the database migrator. -- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. - ```json - { - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:44337" - } - } - } - } - ``` -- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests. Additionally, you need to configure the authentication server as mentioned above. - ```json - { - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:44389" //replace with your host port - } - } - }, - "AuthServer": { - "Authority": "http://192.168.1.37:44337/", //replace with your IP and authentication port - "MetaAddress": "http://192.168.1.37:44337/", - "RequireHttpsMetadata": false, - "Audience": "MyTieredProject" //replace with your application name - } - } - ``` - -{{ else if Architecture == "Microservice" }} - -React Native microservice project configuration - -- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. - ```json - { - "App": { - "EnablePII": true - }, - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:44319" - } - } - } - } - ``` -- Open the `appsettings.Development.json` file in the `.AdministrationService` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. You should also provide the authentication server configuration. Additionally, you need to apply the same process for all services you will use in the React Native application. - ```json - { - "App": { - "EnablePII": true - }, - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:44357" - } - } - }, - "AuthServer": { - "Authority": "http://192.168.1.36:44319/", - "MetaAddress": "http://192.168.1.36:44319/", - "RequireHttpsMetadata": false, - "Audience": "AdministrationService" - } - } - ``` -- Update the `appsettings.json` file in the `.IdentityService` folder. Replace the `localhost` configuration with your local IP address for the React Native application. - ```json - { - //... - "OpenIddict": { - "Applications": { - //... - "ReactNative": { - "RootUrl": "exp://192.168.1.36:19000" - }, - "MobileGateway": { - "RootUrl": "http://192.168.1.36:44347/" - } - //... - } - //... - } - } - ``` -- Finally, update the mobile gateway configurations as follows: - ```json - //gateways/mobile/MyMicroserviceProject.MobileGateway/Properties/launchSettings.json - { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://192.168.1.36:44347" //update with your IP address - } - }, - "profiles": { - //... - "MyMicroserviceProject.MobileGateway": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "applicationUrl": "http://192.168.1.36:44347", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } - } - ``` - ```json - //gateways/mobile/MyMicroserviceProject.MobileGateway/appsettings.json - { - //Update clusters with your IP address - //... - "ReverseProxy": { - //... - "Clusters": { - "AuthServer": { - "Destinations": { - "AuthServer": { - "Address": "http://192.168.1.36:44319/" - } - } - }, - "Administration": { - "Destinations": { - "Administration": { - "Address": "http://192.168.1.36:44357/" - } - } - }, - "Saas": { - "Destinations": { - "Saas": { - "Address": "http://192.168.1.36:44330/" - } - } - }, - "Identity": { - "Destinations": { - "Identity": { - "Address": "http://192.168.1.36:44397/" - } - } - }, - "Language": { - "Destinations": { - "Identity": { - "Address": "http://192.168.1.36:44310/" - } - } - } - } - } - } - ``` - {{ end }} +If the new screen needs to appear at the _root_ level (a new tab or drawer item rather than a child of an existing stack), edit `BottomTabNavigator.tsx` or `DrawerNavigator.tsx` and update the corresponding `BottomTabParamList` / `RootDrawerParamList` type. -Run the backend application(s) as described in the [getting started document](../../../get-started). \ No newline at end of file +The application is up and running. You can continue to develop your application based on this startup template, or follow the [Book Store mobile tutorial](../../../tutorials/mobile/react-native/index.md). diff --git a/docs/en/framework/ui/react-native/manual-backend-configuration.md b/docs/en/framework/ui/react-native/manual-backend-configuration.md new file mode 100644 index 0000000000..0285aa3423 --- /dev/null +++ b/docs/en/framework/ui/react-native/manual-backend-configuration.md @@ -0,0 +1,258 @@ +```json +//[doc-seo] +{ + "Description": "Configure your ABP backend for React Native emulator and device testing using local IP addresses and HTTP when Cloudflare tunnels are unavailable." +} +``` + +```json +//[doc-params] +{ + "Architecture": ["Monolith", "Tiered", "Microservice"] +} +``` + +```json +//[doc-nav] +{ + "Previous": { + "Name": "Running on Device", + "Path": "framework/ui/react-native/running-on-device" + } +} +``` + +# Manual Backend Configuration + +> **When to use this guide:** We recommend this fallback when you cannot use the [Cloudflare tunnel](./running-on-device.md#cloudflare-tunnel-manual-cli) workflow — for example, on **Tiered** or **Microservice** templates (which do not include `tunnel.js`), when corporate firewalls block `cloudflared`, or when developing fully offline. + +> **Prefer simpler options first:** +> +> - Browser testing → [Running on Web](./running-on-web.md) (no backend changes) +> - Pro, non-tiered Monolith on emulator/device → [Running on Device](./running-on-device.md) with `yarn tunnel:api` or the **MobileEmulator** profile + +> React Native does not trust the auto-generated .NET HTTPS certificate. Use **HTTP** during this manual workflow. + +{{ if Architecture == "Monolith" }} + +To disable the HTTPS-only settings of OpenIddict, open the `MyProjectNameHttpApiHostModule` project and add the following code block to the `PreConfigureServices` method: + +```csharp +#if DEBUG + PreConfigure(options => + { + options.UseAspNetCore() + .DisableTransportSecurityRequirement(); + }); +#endif +``` + +{{ else if Architecture == "Tiered" }} + +To disable the HTTPS-only settings of OpenIddict, open the `MyProjectNameAuthServerModule` project and add the following code block to the `PreConfigureServices` method: + +```csharp +#if DEBUG + PreConfigure(options => + { + options.UseAspNetCore() + .DisableTransportSecurityRequirement(); + }); +#endif +``` + +{{ end }} + +> **Important:** Before running the backend application, make sure you have completed the [database migration](../../../get-started?UI=NG&DB=EF&Tiered=No#create-the-database) if you are starting with a fresh database. + +A React Native application running on an Android emulator or a physical phone **cannot connect to the backend** on `localhost`. Bind the backend to `0.0.0.0` so it accepts requests from your local network, then update `Environment.ts` with your local IP address. + +{{ if Architecture == "Monolith" }} + +- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address in the `RootUrl` property with your local IP address. Then, run the database migrator. +- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. + ```json + { + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:44323" //replace with your host port + } + } + } + } + ``` + +Update `react-native/Environment.ts` with your local IP address instead of `localhost`, then start Expo as described in [Running on Device](./running-on-device.md). + +{{ else if Architecture == "Tiered" }} + +- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address in the `RootUrl` property with your local IP address. Then, run the database migrator. +- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. + ```json + { + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:44337" + } + } + } + } + ``` +- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests. Additionally, configure the authentication server as mentioned above. + ```json + { + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:44389" //replace with your host port + } + } + }, + "AuthServer": { + "Authority": "http://192.168.1.37:44337/", //replace with your IP and authentication port + "MetaAddress": "http://192.168.1.37:44337/", + "RequireHttpsMetadata": false, + "Audience": "MyTieredProject" //replace with your application name + } + } + ``` + +Update `react-native/Environment.ts` with your local IP address, then start Expo as described in [Running on Device](./running-on-device.md). + +{{ else if Architecture == "Microservice" }} + +- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. + ```json + { + "App": { + "EnablePII": true + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:44319" + } + } + } + } + ``` +- Open the `appsettings.Development.json` file in the `.AdministrationService` folder. Add this configuration to accept global requests for testing the React Native application in the development environment. You should also provide the authentication server configuration. Additionally, apply the same process for all services you will use in the React Native application. + ```json + { + "App": { + "EnablePII": true + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:44357" + } + } + }, + "AuthServer": { + "Authority": "http://192.168.1.36:44319/", + "MetaAddress": "http://192.168.1.36:44319/", + "RequireHttpsMetadata": false, + "Audience": "AdministrationService" + } + } + ``` +- Update the `appsettings.json` file in the `.IdentityService` folder. Replace the `localhost` configuration with your local IP address for the React Native application. + ```json + { + //... + "OpenIddict": { + "Applications": { + //... + "ReactNative": { + "RootUrl": "exp://192.168.1.36:19000" + }, + "MobileGateway": { + "RootUrl": "http://192.168.1.36:44347/" + } + //... + } + //... + } + } + ``` +- Finally, update the mobile gateway configurations as follows: + ```json + //gateways/mobile/MyMicroserviceProject.MobileGateway/Properties/launchSettings.json + { + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://192.168.1.36:44347" //update with your IP address + } + }, + "profiles": { + //... + "MyMicroserviceProject.MobileGateway": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "applicationUrl": "http://192.168.1.36:44347", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } + ``` + ```json + //gateways/mobile/MyMicroserviceProject.MobileGateway/appsettings.json + { + //Update clusters with your IP address + //... + "ReverseProxy": { + //... + "Clusters": { + "AuthServer": { + "Destinations": { + "AuthServer": { + "Address": "http://192.168.1.36:44319/" + } + } + }, + "Administration": { + "Destinations": { + "Administration": { + "Address": "http://192.168.1.36:44357/" + } + } + }, + "Saas": { + "Destinations": { + "Saas": { + "Address": "http://192.168.1.36:44330/" + } + } + }, + "Identity": { + "Destinations": { + "Identity": { + "Address": "http://192.168.1.36:44397/" + } + } + }, + "Language": { + "Destinations": { + "Language": { + "Address": "http://192.168.1.36:44310/" + } + } + } + } + } + } + ``` + +Update `apps/mobile/react-native/Environment.ts` with your local IP address, then start Expo as described in [Running on Device](./running-on-device.md). + +{{ end }} + +Run the backend application(s) as described in the [getting started document](../../../get-started). diff --git a/docs/en/framework/ui/react-native/running-on-device.md b/docs/en/framework/ui/react-native/running-on-device.md new file mode 100644 index 0000000000..0b45f7a4a4 --- /dev/null +++ b/docs/en/framework/ui/react-native/running-on-device.md @@ -0,0 +1,201 @@ +```json +//[doc-seo] +{ + "Description": "Run your ABP React Native application on an Android emulator, iOS simulator, or physical device using ABP Studio or Cloudflare tunnel." +} +``` + +```json +//[doc-nav] +{ + "Previous": { + "Name": "Running on Web", + "Path": "framework/ui/react-native/running-on-web" + }, + "Next": { + "Name": "Manual Backend Configuration", + "Path": "framework/ui/react-native/manual-backend-configuration" + } +} +``` + +```json +//[doc-params] +{ + "Architecture": ["Monolith", "Tiered", "Microservice"] +} +``` + +# Running on a Device + +You can use this guide when you want to run the app on an **Android emulator**, **iOS simulator**, or **physical device** instead of in the browser. + +For browser testing, see [Running on Web](./running-on-web.md). + +## Prerequisites + +1. Install dependencies in the React Native project folder: + +{{ if Architecture == "Microservice" }} + +- `apps/mobile/react-native/` + +{{ else }} + +- `react-native/` + +{{ end }} + +2. Complete the [database migration](../../../get-started?UI=NG&DB=EF&Tiered=No#create-the-database) and ensure the backend host is running. +3. Set up an emulator or simulator if needed: + - **Android:** [Android Studio Emulator guide](https://docs.expo.dev/workflow/android-studio-emulator/) or [Setting Up Android Emulator Without Android Studio](./setting-up-android-emulator.md) + - **iOS (macOS):** [iOS Simulator guide](https://docs.expo.dev/workflow/ios-simulator/) + +{{ if Architecture == "Monolith" }} + +1. Install **cloudflared** before using the **MobileEmulator** profile or `yarn tunnel:api`. Follow Cloudflare's [Download and install cloudflared](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) guide. The template uses free Quick Tunnels (`*.trycloudflare.com`) and does not require a Cloudflare account or the remaining locally-managed tunnel steps in that document. + +{{ end }} + +## Using ABP Studio + +{{ if Architecture == "Monolith" }} + +### MobileEmulator profile (Recommended) + +**Pro, non-tiered Monolith** solutions include a **MobileEmulator** run profile in the [Solution Runner](../../../studio/running-applications). When you start it, ABP Studio specifically runs: + +1. The backend host +2. **ReactNativeTunnel** — runs `node scripts/tunnel.js` to expose your local backend through a Cloudflare tunnel +3. **ReactNative** — runs `npx expo start --android` (change the `--android` flag to `--ios` in `MobileEmulator.abprun.json` or via the profile edit option in Solution Runner) + +Before the first run: + +1. [Install cloudflared](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) if you have not already (see [Prerequisites](#prerequisites)). +2. Switch to the **MobileEmulator** profile in the Solution Runner. +3. Open `react-native/Environment.ts` and follow the inline comments: **uncomment the tunnel configuration block** and **comment out the localhost constants**. + +Start an Android emulator (or connect a device), then start the **MobileEmulator** profile. + +ABP Studio Solution Runner with MobileEmulator profile selected + +See [Cloudflare tunnel (manual CLI)](#cloudflare-tunnel-manual-cli) below for what the tunnel script does, and [Automate Localhost Access for Expo](https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3) for architecture details and troubleshooting. + +{{ else if Architecture == "Tiered" }} + +> **Note:** The **MobileEmulator** run profile is available only for **Pro, non-tiered** solutions. For tiered architectures, follow [Manual Backend Configuration](./manual-backend-configuration.md) and the [manual CLI steps](#manual-cli-without-mobileemulator-profile) below. + +{{ else }} + +> **Note:** The **MobileEmulator** run profile is not included in the microservice template. Follow [Manual Backend Configuration](./manual-backend-configuration.md) and the [manual CLI steps](#manual-cli-without-mobileemulator-profile) below. + +{{ end }} + +## Cloudflare tunnel (manual CLI) + +{{ if Architecture == "Monolith" }} + +Non-tiered React Native templates ship with a Cloudflare tunnel automation script. The tunnel gives your mobile app a temporary **HTTPS** URL (for example `https://example.trycloudflare.com`) that forwards to your local backend — without reconfiguring Kestrel or OpenIddict over HTTP. + +> **Prerequisite:** Install **cloudflared** on your machine using Cloudflare's [Download and install cloudflared](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) guide before running `yarn tunnel:api` or the **MobileEmulator** profile. + +The template includes: + +| Item | Location | +| ------------------ | ------------------------------------------------------ | +| Tunnel script | `react-native/scripts/tunnel.js` | +| npm script | `yarn tunnel:api` (runs `node scripts/tunnel.js`) | +| Generated config | `react-native/tunnel-config.json` (created at runtime) | +| Environment switch | Inline comments in `react-native/Environment.ts` | + +When `tunnel.js` runs, it: + +1. Starts `cloudflared tunnel --url https://localhost:{api-port}` (port matches your backend host). +2. Captures the generated `*.trycloudflare.com` domain from the `cloudflared` output. +3. Writes `tunnel-config.json` and updates the fallback value in `Environment.ts`. + +### Steps + +1. **Switch `Environment.ts` to tunnel mode.** Open `react-native/Environment.ts` and follow the inline comments: + - **Uncomment** the tunnel configuration block (the block that reads `tunnel-config.json`). + - **Comment out** the localhost `apiUrl` / `appUrl` constants. +2. **Start the backend host** if it is not already running. +3. **Start the tunnel** from the `react-native` folder: + +```bash +yarn tunnel:api +``` + +Or: + +```bash +npm run tunnel:api +``` + +Wait until the script prints `✓ Tunnel domain saved`. + +4. **Start Expo** in a separate terminal: + +```bash +yarn start +``` + +5. Start your Android emulator, iOS simulator, or connect a physical device. + +> **With ABP Studio:** The **MobileEmulator** run profile performs steps 2–4 for you after you update `Environment.ts`. + +See [Automate Localhost Access for Expo](https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3) for OAuth redirect considerations, troubleshooting, and development-build guidance. + +If Cloudflare tunnels are unavailable, use [Manual Backend Configuration](./manual-backend-configuration.md) as a fallback. + +{{ else if Architecture == "Tiered" }} + +Tiered templates do not include `tunnel.js` or the `tunnel:api` script. Configure the backend using [Manual Backend Configuration](./manual-backend-configuration.md), or adapt the [Cloudflare tunnel guide](https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3) for your auth server and API host ports. + +{{ else }} + +Microservice templates do not include `tunnel.js` or the `tunnel:api` script. Configure the backend using [Manual Backend Configuration](./manual-backend-configuration.md), or adapt the [Cloudflare tunnel guide](https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3) for your auth server and mobile gateway ports. + +{{ end }} + +## Manual CLI (without MobileEmulator profile) + +{{ if Architecture == "Monolith" }} + +If you are not using the **MobileEmulator** profile, prefer the [Cloudflare tunnel workflow](#cloudflare-tunnel-manual-cli) above. Only use [Manual Backend Configuration](./manual-backend-configuration.md) when tunnels are unavailable. + +{{ else }} + +1. Configure the backend as described in [Manual Backend Configuration](./manual-backend-configuration.md). +2. Open the `Environment.ts` file in the React Native folder and replace the `localhost` address in the `apiUrl` and `issuer` properties with your local IP address: + +{{ if Architecture == "Tiered" }} + +> Make sure that `issuer` matches the running address of the `.AuthServer` project, and `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project. + +{{ else }} + +> Make sure that `issuer` matches the running address of the `.AuthServer` project, and `apiUrl` matches the running address of the mobile gateway. + +{{ end }} + +1. Run `yarn start`, `yarn android`, or `yarn ios`. + +{{ end }} + +> The React Native application was generated with [Expo](https://expo.io/). Expo is a set of tools built around React Native to help you quickly start an app, and it includes many features. + +Expo CLI showing QR code and platform launch options + +You can start the application on an Android emulator, an iOS simulator, or a physical phone by scanning the QR code or by choosing the corresponding option in the Expo CLI. + +Enter **admin** as the username and **1q2w3E\*** as the password to log in to the application. + +### Android Studio + +1. Start the emulator in **Android Studio** before running `yarn start`, `yarn android`, or `npm start`. +2. Press **a** in the Expo CLI to open on Android. + +### iOS Simulator + +1. Press **i** in the Expo CLI to open the iOS simulator (macOS only). diff --git a/docs/en/framework/ui/react-native/running-on-web.md b/docs/en/framework/ui/react-native/running-on-web.md new file mode 100644 index 0000000000..60286f51af --- /dev/null +++ b/docs/en/framework/ui/react-native/running-on-web.md @@ -0,0 +1,116 @@ +```json +//[doc-seo] +{ + "Description": "Run your ABP React Native application in the browser using ABP Studio or Expo Web with local HTTPS." +} +``` + +```json +//[doc-params] +{ + "Architecture": ["Monolith", "Tiered", "Microservice"] +} +``` + +```json +//[doc-nav] +{ + "Previous": { + "Name": "Overview", + "Path": "framework/ui/react-native" + }, + "Next": { + "Name": "Running on Device", + "Path": "framework/ui/react-native/running-on-device" + } +} +``` + +# Running on Web + +Running the React Native app in a browser is the **fastest way to test** login, navigation, and API integrations. No emulator, Cloudflare tunnel, or manual backend configuration is required. + +## React Native project folder + +{{ if Architecture == "Microservice" }} + +- `apps/mobile/react-native/` + +{{ else }} + +- `react-native/` + +{{ end }} + +Install dependencies once with `yarn install` or `npm install` in that folder. + +## Using ABP Studio (Recommended) + +Solutions created with ABP Studio include a **Default** run profile in the [Solution Runner](../../../studio/running-applications). + +{{ if Architecture == "Monolith" }} + +React Native Default Profile + +{{ else if Architecture == "Tiered" }} + +React Native Default Profile + +{{ else }} + +React Native Default Profile + +{{ end }} + +### One-time setup: Initialize Solution + +Run the **Initialize Solution** task from the **Tasks** tab if you have not already. For React Native solutions, this task also creates local SSL certificates (`localhost.pem` and `localhost-key.pem`) in the React Native folder using [mkcert](https://github.com/FiloSottile/mkcert). These certificates are required by the **Default** profile's local SSL proxy. + +### Start the Default profile + +When you start the **Default** profile, ABP Studio runs these main applications: + +1. The backend host +2. **ReactNative.LocalSslProxy** — terminates HTTPS on **`https://localhost:8443`** and forwards to Expo Web on port `8081` +3. **ReactNative** — runs `npx expo start --web` and opens **`https://localhost:8443`** in your browser + +The solution template already adds `https://localhost:8443` to backend **CorsOrigins** and **RedirectAllowedUrls**, so no manual backend configuration is required. + +{{ if Architecture != "Microservice" }} + +React Native Web ABP Studio + +{{ end }} + +You can enter **admin** as the username and **1q2w3E\*** as the password to log in. + +## Manual setup (without ABP Studio) + +If you are not using ABP Studio, use Expo Web with a local HTTPS proxy as described in the [Expo local HTTPS development guide](https://docs.expo.dev/guides/local-https-development/). + +1. Navigate to the React Native folder and install dependencies if you have not already. +2. Generate SSL certificates in the React Native folder: + +```bash +mkcert localhost +``` + +3. Start Expo Web: + +```bash +yarn web +``` + +4. In a separate terminal, start the local SSL proxy (port `8443` matches the ABP Studio Default profile): + +```bash +npx local-ssl-proxy --source 8443 --target 8081 --cert localhost.pem --key localhost-key.pem +``` + +Alternatively, run `yarn create:local-proxy` and set `SOURCE_PORT=8443` if your template uses a different default port. + +1. Open **`https://localhost:8443`** in your browser. + +## Next steps + +To test on an Android emulator, iOS simulator, or physical device, continue with [Running on Device](./running-on-device.md). diff --git a/docs/en/framework/ui/react-native/setting-up-android-emulator.md b/docs/en/framework/ui/react-native/setting-up-android-emulator.md index 495197a0da..963c6d5b92 100644 --- a/docs/en/framework/ui/react-native/setting-up-android-emulator.md +++ b/docs/en/framework/ui/react-native/setting-up-android-emulator.md @@ -5,6 +5,16 @@ } ``` +````json +//[doc-nav] +{ + "Previous": { + "Name": "Running on Device", + "Path": "framework/ui/react-native/running-on-device" + } +} +```` + # Setting Up Android Emulator Without Android Studio (Windows, macOS, Linux) This guide walks you through installing and running an Android emulator **without Android Studio**, using only the **Android Command Line Tools**. diff --git a/docs/en/framework/ui/react/index.md b/docs/en/framework/ui/react/index.md index 115f39bb00..e90a53c89c 100644 --- a/docs/en/framework/ui/react/index.md +++ b/docs/en/framework/ui/react/index.md @@ -28,12 +28,12 @@ The React UI template is built with: | [Vitest](https://vitest.dev/) | Unit testing | | [OpenID Connect / OIDC](https://openid.net/connect/) | Authentication against the ABP Auth Server | -The template also includes ABP-specific NPM packages: +The template also includes ABP-specific NPM packages. These packages are maintained by ABP and published on npm; use the following links to view their package details: -- [`@volo/abp-app-config`](https://www.npmjs.com/package/@volo/abp-app-config) -- [`@volo/abp-oidc-auth`](https://www.npmjs.com/package/@volo/abp-oidc-auth) -- [`@volo/abp-react-app-config`](https://www.npmjs.com/package/@volo/abp-react-app-config) -- [`@volo/abp-react-oidc-auth`](https://www.npmjs.com/package/@volo/abp-react-oidc-auth) +- [View `@volo/abp-app-config` on npm](https://www.npmjs.com/package/@volo/abp-app-config) +- [View `@volo/abp-oidc-auth` on npm](https://www.npmjs.com/package/@volo/abp-oidc-auth) +- [View `@volo/abp-react-app-config` on npm](https://www.npmjs.com/package/@volo/abp-react-app-config) +- [View `@volo/abp-react-oidc-auth` on npm](https://www.npmjs.com/package/@volo/abp-react-oidc-auth) ## React App and Admin Console diff --git a/docs/en/images/pen-test-alert-list-10.4.png b/docs/en/images/pen-test-alert-list-10.4.png new file mode 100644 index 0000000000..b86b64af75 Binary files /dev/null and b/docs/en/images/pen-test-alert-list-10.4.png differ diff --git a/docs/en/images/react-native-app.gif b/docs/en/images/react-native-app.gif new file mode 100644 index 0000000000..7cf95f932d Binary files /dev/null and b/docs/en/images/react-native-app.gif differ diff --git a/docs/en/images/rn-default-profile.png b/docs/en/images/rn-default-profile.png new file mode 100644 index 0000000000..eda9871dfc Binary files /dev/null and b/docs/en/images/rn-default-profile.png differ diff --git a/docs/en/images/rn-expo-qr.png b/docs/en/images/rn-expo-qr.png new file mode 100644 index 0000000000..e651d73888 Binary files /dev/null and b/docs/en/images/rn-expo-qr.png differ diff --git a/docs/en/images/rn-microservice-default-profile.png b/docs/en/images/rn-microservice-default-profile.png new file mode 100644 index 0000000000..e5859c3971 Binary files /dev/null and b/docs/en/images/rn-microservice-default-profile.png differ diff --git a/docs/en/images/rn-mobileEmulator-profile.png b/docs/en/images/rn-mobileEmulator-profile.png new file mode 100644 index 0000000000..2824413950 Binary files /dev/null and b/docs/en/images/rn-mobileEmulator-profile.png differ diff --git a/docs/en/images/rn-select-legacy.png b/docs/en/images/rn-select-legacy.png new file mode 100644 index 0000000000..d50b7f396b Binary files /dev/null and b/docs/en/images/rn-select-legacy.png differ diff --git a/docs/en/images/rn-select-modern.png b/docs/en/images/rn-select-modern.png new file mode 100644 index 0000000000..1b301fadde Binary files /dev/null and b/docs/en/images/rn-select-modern.png differ diff --git a/docs/en/images/rn-tiered-default-profile.png b/docs/en/images/rn-tiered-default-profile.png new file mode 100644 index 0000000000..54e63f81ca Binary files /dev/null and b/docs/en/images/rn-tiered-default-profile.png differ diff --git a/docs/en/images/rn-web-studio.png b/docs/en/images/rn-web-studio.png new file mode 100644 index 0000000000..81561a92ec Binary files /dev/null and b/docs/en/images/rn-web-studio.png differ diff --git a/docs/en/low-code/custom-endpoints.md b/docs/en/low-code/custom-endpoints.md index ad0057df70..c75f92461f 100644 --- a/docs/en/low-code/custom-endpoints.md +++ b/docs/en/low-code/custom-endpoints.md @@ -1,112 +1,138 @@ ```json //[doc-seo] { - "Description": "Define custom REST API endpoints with JavaScript handlers in the ABP Low-Code System. Create dynamic APIs without writing C# controllers." + "Description": "Define JavaScript-backed custom REST endpoints in the ABP Low-Code System without writing custom .NET controllers." } ``` # Custom Endpoints -Custom Endpoints allow you to define REST API routes with server-side JavaScript handlers directly in `model.json`. Each endpoint is registered as an ASP.NET Core endpoint at startup and supports hot-reload when the model changes. +> **Preview:** Custom endpoint descriptors and scripting helpers are part of the preview Low-Code System. Names, validation rules, and runtime behavior may change before general availability. -## Defining Endpoints +Use generated page CRUD APIs for normal dynamic entity pages. Custom endpoints are an advanced option for exposing small model-owned REST APIs that do not map to standard list, get, create, update, delete, export, file, or attachment operations. + +Custom endpoints are defined in JSON descriptor files or through the Low-Code Designer. Each endpoint executes server-side JavaScript and is registered as an ASP.NET Core route. -Add endpoints to the `endpoints` array in `model.json`: +## Defining Endpoints ```json { "endpoints": [ { - "name": "GetProductStats", - "route": "/api/custom/products/stats", + "name": "GetCampaignStats", + "route": "/api/custom/campaigns/stats", "method": "GET", - "description": "Get product statistics", - "requireAuthentication": false, - "javascript": "var count = await db.count('LowCodeDemo.Products.Product');\nreturn ok({ totalProducts: count });" + "description": "Get campaign statistics", + "requireAuthentication": true, + "requiredPermissions": ["Acme.Campaigns"], + "javascript": "var count = await db.count('Acme.Campaigns.Campaign');\nreturn ok({ totalCampaigns: count });" } ] } ``` -### Endpoint Descriptor +## Endpoint Descriptor | Field | Type | Default | Description | |-------|------|---------|-------------| -| `name` | string | **Required** | Unique endpoint name | -| `route` | string | **Required** | URL route pattern (supports `{parameters}`) | -| `method` | string | `"GET"` | HTTP method: `GET`, `POST`, `PUT`, `DELETE` | -| `javascript` | string | **Required** | JavaScript handler code | -| `description` | string | null | Description for documentation | -| `requireAuthentication` | bool | `true` | Require authenticated user | -| `requiredPermissions` | string[] | null | Required permission names | +| `name` | string | Required | Unique endpoint identifier | +| `route` | string | Required | URL route pattern; must start with `/` and can contain `{parameters}` | +| `method` | string | `GET` | `GET`, `POST`, `PUT`, `DELETE`, or `PATCH` | +| `javascript` | string | Required | JavaScript handler code | +| `description` | string | null | Optional designer/documentation text | +| `requireAuthentication` | bool | `true` | Whether the caller must be authenticated | +| `requiredPermissions` | string[] | null | Permission names required to call the endpoint | -## Route Parameters +Permission checks require an authorized user even when `requireAuthentication` is set to `false`. Keep endpoints authenticated by default and use `requireAuthentication: false` only for intentionally public APIs without `requiredPermissions`. -Use `{paramName}` syntax in the route. Access values via the `route` object: +## Route and Request Data + +Use `{paramName}` syntax for route parameters. Endpoint scripts can read request data through globals: + +| Variable | Description | +|----------|-------------| +| `request` | Full request object | +| `route` | Route values, for example `route.id` | +| `params` | Alias for `route` | +| `query` | Query string values, for example `query.q` | +| `body` | Parsed request body | +| `headers` | Selected safe request headers | ```json { - "name": "GetProductById", - "route": "/api/custom/products/{id}", + "name": "GetCampaignById", + "route": "/api/custom/campaigns/{id}", "method": "GET", - "javascript": "var product = await db.get('LowCodeDemo.Products.Product', route.id);\nif (!product) { return notFound('Product not found'); }\nreturn ok({ id: product.Id, name: product.Name, price: product.Price });" + "javascript": "var campaign = await db.get('Acme.Campaigns.Campaign', route.id);\nif (!campaign) { return notFound('Campaign not found'); }\nreturn ok({ id: campaign.Id, name: campaign.Name });" } ``` -## JavaScript Context +For non-GET requests, `body` is parsed when a body is present and remains subject to the configured request size limit. The `headers` object intentionally contains only selected request headers such as `Content-Type`, `Accept`, `Accept-Language`, and `X-Requested-With`. + +## Response Helpers + +Endpoint scripts can return plain data, an endpoint response object, or one of the response helpers. + +| Function | HTTP status | Response kind | +|----------|-------------|---------------| +| `ok(data, headers?)` | 200 | JSON | +| `okText(text, contentType?)` | 200 | Text | +| `okBinary(base64Data, contentType?)` | 200 | Binary from base64 | +| `created(data, headers?)` | 201 | JSON | +| `noContent()` | 204 | Empty | +| `badRequest(message)` | 400 | JSON error | +| `unauthorized(message)` | 401 | JSON error | +| `forbidden(message)` | 403 | JSON error | +| `notFound(message)` | 404 | JSON error | +| `error(message)` | 500 | JSON error | + +For custom status codes or response metadata, return an object with response fields: + +```javascript +return { + statusCode: 202, + kind: 'json', + data: { accepted: true }, + headers: { 'x-trace-id': guid() } +}; +``` -Inside custom endpoint scripts, you have access to: +Response kinds are: -### Request Context +| Kind | Description | +|------|-------------| +| `json` | JSON serialization, default | +| `text` | Plain text | +| `binaryBase64` | Base64-encoded binary payload | -| Variable | Description | -|----------|-------------| -| `request` | Full request object | -| `route` | Route parameter values (e.g., `route.id`) | -| `params` | Alias for route parameters | -| `query` | Query string parameters (e.g., `query.q`, `query.page`) | -| `body` | Request body (for POST/PUT) | -| `headers` | Request headers | -| `user` | Current user (same as `context.currentUser` in [Interceptors](interceptors.md)) | -| `email` | Email sender (same as `context.emailSender` in [Interceptors](interceptors.md)) | - -### Response Helpers - -| Function | HTTP Status | Description | -|----------|-------------|-------------| -| `ok(data)` | 200 | Success response with data | -| `created(data)` | 201 | Created response with data | -| `noContent()` | 204 | No content response | -| `badRequest(message)` | 400 | Bad request response | -| `unauthorized(message)` | 401 | Unauthorized response | -| `forbidden(message)` | 403 | Forbidden response | -| `notFound(message)` | 404 | Not found response | -| `error(message)` | 500 | Internal server error response | -| `response(statusCode, data, error)` | Custom | Custom status code response | - -### Logging - -| Function | Description | -|----------|-------------| -| `log(message)` | Log an informational message | -| `logWarning(message)` | Log a warning message | -| `logError(message)` | Log an error message | +## Script Services -### Database API +Custom endpoint scripts use the same common [Scripting API](scripting-api.md) services as other low-code scripts: -The full [Scripting API](scripting-api.md) (`db` object) is available for querying and mutating data. +| Service | Example | +|---------|---------| +| `db` | Query or mutate dynamic entities | +| `user` / `currentUser` | Read current user information | +| `tenant` / `currentTenant` | Read tenant information | +| `auth` / `authorization` | Check permissions | +| `settings`, `features`, `config` | Read allowed settings, features, and app configuration | +| `http` | Call allowed outbound HTTP services | +| `email` | Send or queue email | +| `events`, `jobs` | Publish distributed events or enqueue background jobs | +| `files`, `images`, `attachments` | Work with low-code files and record attachments | +| `log`, `logWarning`, `logError` | Write logs | ## Examples -### Get Statistics +### Statistics ```json { - "name": "GetProductStats", - "route": "/api/custom/products/stats", + "name": "GetCampaignStats", + "route": "/api/custom/campaigns/stats", "method": "GET", - "requireAuthentication": false, - "javascript": "var totalCount = await db.count('LowCodeDemo.Products.Product');\nvar avgPrice = totalCount > 0 ? await db.query('LowCodeDemo.Products.Product').average(p => p.Price) : 0;\nreturn ok({ totalProducts: totalCount, averagePrice: avgPrice });" + "requireAuthentication": true, + "javascript": "var campaignQuery = await db.query('Acme.Campaigns.Campaign');\nvar total = await campaignQuery.count();\nvar active = await campaignQuery.where(c => c.Status === 1).count();\nreturn ok({ total: total, active: active });" } ``` @@ -114,36 +140,98 @@ The full [Scripting API](scripting-api.md) (`db` object) is available for queryi ```json { - "name": "SearchCustomers", - "route": "/api/custom/customers/search", + "name": "SearchCampaigns", + "route": "/api/custom/campaigns/search", "method": "GET", "requireAuthentication": true, - "javascript": "var searchTerm = query.q || '';\nvar customers = await db.query('LowCodeDemo.Customers.Customer')\n .where(c => c.Name.toLowerCase().includes(searchTerm.toLowerCase()))\n .take(10)\n .toList();\nreturn ok(customers.map(c => ({ id: c.Id, name: c.Name, email: c.EmailAddress })));" + "javascript": "var q = query.q || '';\nvar campaignQuery = await db.query('Acme.Campaigns.Campaign');\nvar rows = await campaignQuery\n .where(c => c.Name.toLowerCase().includes(q.toLowerCase()))\n .take(10)\n .toList();\nreturn ok(rows.map(c => ({ id: c.Id, name: c.Name })));" } ``` -### Dashboard Summary +### Create with Validation ```json { - "name": "GetDashboardSummary", - "route": "/api/custom/dashboard", - "method": "GET", + "name": "CreateCampaignDraft", + "route": "/api/custom/campaigns/draft", + "method": "POST", "requireAuthentication": true, - "javascript": "var productCount = await db.count('LowCodeDemo.Products.Product');\nvar customerCount = await db.count('LowCodeDemo.Customers.Customer');\nvar orderCount = await db.count('LowCodeDemo.Orders.Order');\nreturn ok({ products: productCount, customers: customerCount, orders: orderCount, user: user.isAuthenticated ? user.userName : 'Anonymous' });" + "requiredPermissions": ["Acme.Campaigns.Create"], + "javascript": "if (!body.name) { return badRequest('Name is required.'); }\nvar record = await db.insert('Acme.Campaigns.Campaign', { Name: body.name, Status: 0 });\nreturn created({ id: record.Id, name: record.Name });" } ``` -## Authentication and Authorization +## Testing Endpoint Scripts + +The Low-Code Designer endpoint editor includes **Test JavaScript**. Use it to run the current editor content without saving it. + +The dry-run request editor lets you provide: + +* HTTP method +* Request path +* Route values +* Query values +* Headers +* Body JSON +* Outbound HTTP mocks + +Dry-run execution evaluates the endpoint descriptor, request context, script, authentication metadata, and required permissions against the current user. It returns the same response shape that a real endpoint execution would return. + +Side effects are captured instead of being sent to external systems: + +| Operation | Dry-run behavior | +|-----------|------------------| +| Database writes | Rolled back | +| Email send or queue | Captured under **Captured Side Effects** | +| Event publish | Captured under **Captured Side Effects** | +| Background job enqueue | Captured under **Captured Side Effects** | +| Outbound HTTP | Matched against HTTP mocks | +| File, image, and attachment operations | Captured without persisting files | + +If a script calls the `http` helper and no mock matches the method and URL, the result contains a mock miss instead of sending a real HTTP request. + +## Response Policy + +Dynamic endpoint responses are validated by `LowCode:Scripting:EndpointResponse`. + +```json +{ + "LowCode": { + "Scripting": { + "EndpointResponse": { + "MaxBodyBytes": 1048576, + "AllowedContentTypes": [ + "application/json", + "text/plain", + "application/octet-stream" + ], + "BlockedHeaders": [ + "Set-Cookie", + "Content-Length", + "Content-Type" + ] + } + } + } +} +``` + +Default blocked headers also include hop-by-hop headers such as `Connection`, `Transfer-Encoding`, and `Upgrade`. + +`Content-Type` is blocked as a custom response header. Choose the response kind or `contentType` field instead of setting a raw `Content-Type` header from script. + +## Security Notes -| Setting | Behavior | -|---------|----------| -| `requireAuthentication: false` | Endpoint is publicly accessible | -| `requireAuthentication: true` | User must be authenticated | -| `requiredPermissions: ["MyApp.Products"]` | User must have the specified permissions | +* Prefer authenticated endpoints with explicit `requiredPermissions`. +* Treat endpoints with `requireAuthentication: false` and no `requiredPermissions` as public API surface. +* Keep endpoint scripts small and focused. +* Validate route, query, and body input before using it. +* Use `take()` for list queries. +* Use the configured HTTP, email, file, and response limits for untrusted integrations. ## See Also * [Scripting API](scripting-api.md) +* [Script Actions](script-actions.md) * [Interceptors](interceptors.md) -* [model.json Structure](model-json.md) +* [Model Descriptor Files](model-json.md) diff --git a/docs/en/low-code/designer.md b/docs/en/low-code/designer.md new file mode 100644 index 0000000000..5f6380cc81 --- /dev/null +++ b/docs/en/low-code/designer.md @@ -0,0 +1,135 @@ +```json +//[doc-seo] +{ + "Description": "Use the ABP Admin Console Low-Code Designer to create dynamic entities, pages, forms, relations, filters, permissions, and model health checks." +} +``` + +# Low-Code Designer + +> **Preview:** The Low-Code Designer is part of the preview Low-Code System. Designer screens, metadata fields, and validation rules may change before general availability. + +The Low-Code Designer is available in ABP Admin Console. It is the main UI for building and maintaining low-code descriptor metadata. + +```text +/admin-console/lowcode-designer +``` + +The designer works with layered descriptor metadata. In development, generated projects include source-controlled descriptor files under `_Dynamic` and a generated initializer. The designer can also persist changes to the database JSON layer, depending on the selected layer and permissions. + +![Low-Code Designer overview](images/designer-overview.png) + +## Permissions and Layers + +Designer APIs require the low-code designer permission. Users who can open runtime pages do not automatically get designer access. + +The selected layer controls whether the designer can save changes. Read-only layers can be inspected but not mutated, and the designer blocks edits to layers that are not currently selected. Check the active layer before changing entities, pages, forms, scripts, or permissions. + +## Sections + +| Section | Purpose | +|---------|---------| +| Data | Entities, enums, properties, relations, inherited fields, and reference entities | +| Actions | Script-backed custom endpoints, event handlers, background jobs, workers, and model actions | +| Pages | Runtime pages, menu placement, page type, grid/card fields, filters, sorting, dashboards, and linked forms | +| Forms | Create/edit forms, tabs, groups, fields, controls, defaults, and actions | +| Permissions | Generated permission names and access control | +| Health | Model validation and runtime readiness checks | + +## Data + +Use **Data** to define the domain model. + +Entities contain properties, display names, display property configuration, inherited audit fields, relations, and optional interceptors. Enums are created once and then used by enum properties. + +![Entity summary in the designer](images/designer-entity.png) + +![Entity property list](images/designer-properties.png) + +### Relations + +Relations are driven by foreign key properties. The designer shows direct N to 1 relations and many-to-many relations that are modeled through junction entities. + +![Relation overview](images/designer-relations.png) + +For reference entities such as `IdentityUser`, register the entity in the generated `_Dynamic` initializer. The designer and runtime can then show friendly display values instead of raw IDs. + +## Pages + +Use **Pages** to expose an entity in the React runtime. + +Pages can define data grid, kanban, calendar, gallery, standalone form, and dashboard experiences. A data grid page can define: + +* Title and icon +* Menu group and order +* Entity +* Create and edit forms +* Visible grid/card fields +* Export fields for Excel, CSV, download-link columns, and file bundles +* Field labels and column widths +* Default sorting +* Filter fields and defaults +* File/image export defaults when the page has exportable file fields +* Whether file bundle ZIP export is allowed + +Kanban pages add `groupByProperty`, calendar pages add date/time properties, gallery pages can use an image property, form pages reference a named form, and dashboard pages define rows and visualizations. + +![Page setup](images/designer-page-filters.png) + +Pages are exposed in React under `/dynamic/` and can also appear as dynamic menu items. + +The **View Fields** section controls what users see in the runtime view. The separate **Export Fields** section controls what may leave the page through Excel, CSV, download-link columns, or file bundles. A visible field can be excluded from export, and an exportable field can be hidden from the page but still available through **Export options > All exportable fields**. Use the bulk actions to copy visible fields into the export policy, enable all fields, or disable all fields. + +Open **Export Fields > More settings** only when you need custom export details. If **Use default export settings** is enabled, export uses display labels, the view field order, file name output, and file bundle export enabled. Turn it off to customize export labels, assign a separate export order, choose **Default File/Image Output**, or disable **Allow file bundle export**. File/image output controls appear only when the page has file or image fields, and they stay inactive until at least one file/image field is exportable. + +## Forms + +Use **Forms** to define create and edit experiences. + +Forms can contain: + +* Tabs +* Groups +* Ordered fields +* Control types +* Placeholder and help text +* Default values +* Required and validation behavior +* Conditional rules for hide/show, enable/disable, and set value behavior +* Save actions such as "save and new" + +![Form setup](images/designer-forms.png) + +## Filters + +Filters are configured per page and rendered by the React runtime. The runtime uses type-specific operators to keep the UI simple: + +* String fields use text operators. +* Number and money fields use range and comparison operators. +* Date fields use date labels such as on, after, and before. +* Boolean fields use `All / Yes / No`. +* File and image fields use `Has value` with `All / Yes / No`. + +The URL query parameter keeps the existing `lcFilters` format, so bookmarked filtered pages continue to work. + +## Permissions + +Dynamic permissions are generated for entities and pages. Use the **Permissions** section to review names and grant access through the normal ABP permission management UI. + +Generated pages and menus are permission-aware. If a user cannot access a page, the runtime does not show the menu item and API calls remain protected by backend authorization. + +## Actions and Scripts + +Use **Actions** only when descriptor metadata and standard CRUD behavior are not enough. The scripting surface can define custom HTTP endpoints, distributed event handlers, background jobs, and scheduled background workers. Scripts run server-side and use the [Scripting API](scripting-api.md). + +JavaScript editors in the Designer provide syntax highlighting, service autocomplete, entity name autocomplete, entity property autocomplete, enum autocomplete, and an **Available context** list. The context list reflects the services enabled for the selected script type. The `fileFields` and `imageFields` context items are selector trees for `File` and `Image` properties used by the file and image helpers; they are not lists of every entity property. + +Endpoint and event handler editors include **Test JavaScript**. Dry-run execution runs the current editor content without saving it, rolls back database writes, captures email/event/job/file side effects, and resolves outbound HTTP calls through test mocks. See [Script Actions](script-actions.md) for action descriptors and dry-run behavior. + +## Health + +Use **Health** before shipping changes. It helps catch missing display properties, invalid relation targets, form/page references, script problems, and other model issues that would otherwise surface at runtime. + +## Source Control + +For source-controlled models, keep the `_Dynamic` descriptor files and the generated initializer in your application repository. Use [Model Descriptor Files](model-json.md), [Attributes & Fluent API](fluent-api.md), and [Reference Entities](reference-entities.md) for advanced editing and integration details. diff --git a/docs/en/low-code/fluent-api.md b/docs/en/low-code/fluent-api.md index 5aa38fe66a..74c0caf118 100644 --- a/docs/en/low-code/fluent-api.md +++ b/docs/en/low-code/fluent-api.md @@ -1,13 +1,15 @@ ```json //[doc-seo] { - "Description": "Define dynamic entities using C# attributes and configure them with the Fluent API in the ABP Low-Code System. The primary way to build auto-generated admin panels." + "Description": "Define dynamic entities using .NET attributes and configure them with the Fluent API in the ABP Low-Code System for advanced source-controlled model configuration." } ``` # Attributes & Fluent API -C# Attributes and the Fluent API are the **recommended way** to define dynamic entities. They provide compile-time checking, IntelliSense, refactoring support, and keep your entity definitions close to your domain code. +> **Preview:** Attributes and Fluent API configuration for the Low-Code System are preview APIs. Prefer the designer for normal modeling work, and review release notes before relying on these APIs in long-lived integrations. + +Use the [Low-Code Designer](designer.md) for day-to-day entity, page, form, and filter work. C# attributes and the Fluent API are advanced configuration options for teams that need source-controlled model definitions, compile-time checking, or programmatic overrides. ## Quick Start @@ -37,7 +39,7 @@ dotnet ef migrations add Added_Product dotnet ef database update ``` -You now have a complete Product management page with data grid, create/edit modals, search, sorting, and pagination. +After migrations and runtime startup, the React low-code runtime can render a Product management page with data grid, create/edit forms, search, sorting, filters, and pagination. ### Step 3: Add Relationships @@ -71,7 +73,7 @@ The `Order` page now has a foreign key dropdown for Customer, and `OrderLine` is The Low-Code System uses a layered configuration model. From lowest to highest priority: 1. **Code Layer** — C# classes with `[DynamicEntity]` and other attributes -2. **JSON Layer** — `model.json` file (see [model.json Structure](model-json.md)) +2. **JSON Descriptor Layer** — source-controlled descriptor files under `_Dynamic` (see [Model Descriptor Files](model-json.md)) 3. **Fluent Layer** — `AbpDynamicEntityConfig.EntityConfigurations` A `DefaultLayer` runs last to fill in any missing values with conventions. @@ -257,7 +259,7 @@ Enum values can be localized using ABP's localization system. Add localization k } ``` -The Blazor UI automatically uses these localization keys for enum dropdowns and display values. If no localization key is found, the enum member name is used as-is. +The React runtime automatically uses these localization keys for enum dropdowns and display values. If no localization key is found, the enum member name is used as-is. ## Fluent API @@ -265,7 +267,7 @@ The Fluent API has the **highest priority** in the configuration system. Use `Ab ### Basic Usage -Configure in your Low-Code Initializer (e.g. `MyAppLowCodeInitializer`): +Configure in startup initialization code (for example `MyAppLowCodeInitializer`): ````csharp public static class MyAppLowCodeInitializer @@ -378,23 +380,23 @@ entity.Interceptors.Add(new CommandInterceptorDescriptor("Create") ## Assembly Registration -Register assemblies containing `[DynamicEntity]` classes in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer): +Register assemblies containing `[DynamicEntity]` classes in startup initialization code: ````csharp AbpDynamicEntityConfig.SourceAssemblies.Add( new DynamicEntityAssemblyInfo( typeof(MyDomainModule).Assembly, rootNamespace: "MyApp", - projectRootPath: sourcePath // For model.json hot-reload + projectRootPath: sourcePath // For descriptor hot-reload ) ); ```` | Parameter | Description | |-----------|-------------| -| `assembly` | The assembly containing `[DynamicEntity]` classes and/or `model.json` | +| `assembly` | The assembly containing `[DynamicEntity]` classes and/or descriptor metadata | | `rootNamespace` | Root namespace for the assembly (used for embedded resource lookup) | -| `projectRootPath` | Path to the Domain project source folder (enables `model.json` hot-reload in development) | +| `projectRootPath` | Path to the Domain project source folder (enables descriptor hot-reload in development) | You can also register entity types directly: @@ -403,21 +405,21 @@ AbpDynamicEntityConfig.DynamicEntityTypes.Add(typeof(Product)); AbpDynamicEntityConfig.DynamicEnumTypes.Add(typeof(OrganizationType)); ```` -## Combining with model.json +## Combining with JSON Descriptors -Attributes and model.json work together seamlessly. A common pattern: +Attributes and JSON descriptors work together seamlessly. A common pattern: 1. **Define core entities** with C# attributes (compile-time safety) -2. **Add additional entities** via model.json (no recompilation needed) +2. **Add additional entities** via descriptor files (no recompilation needed) 3. **Fine-tune configuration** with Fluent API (overrides everything) The three-layer system merges all definitions: ``` -Fluent API (highest) > JSON (model.json) > Code (Attributes) > Defaults (lowest) +Fluent API (highest) > JSON descriptors > Code (Attributes) > Defaults (lowest) ``` -For example, if an attribute sets `[DynamicPropertyUnique]` and model.json sets `"isUnique": false`, the JSON value wins because JSON layer has higher priority than Code layer. +For example, if an attribute sets `[DynamicPropertyUnique]` and a descriptor sets `"isUnique": false`, the JSON value wins because the JSON descriptor layer has higher priority than the Code layer. ## End-to-End Example @@ -497,7 +499,7 @@ public class OrderLine : DynamicEntityBase } ```` -Register everything in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer): +Register everything in startup initialization code: ````csharp public static class MyAppLowCodeInitializer @@ -562,11 +564,12 @@ public class MyAppDbContextFactory : IDesignTimeDbContextFactory // ... BuildConfiguration method ... } +```` This gives you four auto-generated pages (Customers, Products, Orders with nested OrderLines), complete with permissions, menu items, foreign key lookups, and interceptor-based business rules. ## See Also -* [model.json Structure](model-json.md) +* [Model Descriptor Files](model-json.md) * [Reference Entities](reference-entities.md) * [Interceptors](interceptors.md) diff --git a/docs/en/low-code/foreign-access.md b/docs/en/low-code/foreign-access.md index f7545dad05..4c6f87e956 100644 --- a/docs/en/low-code/foreign-access.md +++ b/docs/en/low-code/foreign-access.md @@ -7,6 +7,10 @@ # Foreign Access +> **Preview:** Foreign access metadata is part of the preview Low-Code System. Relation behavior and designer options may change before general availability. + +Use the [Low-Code Designer](designer.md) to review relation metadata visually. This page explains the advanced configuration values that control how related dynamic entities can be reached from runtime pages. + Foreign Access controls how related **dynamic entities** can be accessed through foreign key relationships. It determines whether users can view or manage related data directly from the **target entity's** UI. > **Important:** Foreign Access only works between **dynamic entities**. It does not apply to [reference entities](reference-entities.md) because they are read-only and don't have UI pages. @@ -52,7 +56,7 @@ AbpDynamicEntityConfig.EntityConfigurations.Configure( ); ```` -## Configuring in model.json +## Configuring in JSON Descriptors Set the `access` field on a foreign key property: @@ -108,7 +112,7 @@ Set the `access` field on a foreign key property: When foreign access is configured between two **dynamic entities**: -![Actions menu showing foreign access items (Order, Visited Country, etc.)](images/actions-menu.png) +![Relation overview in the Low-Code Designer](images/designer-relations.png) ### `ForeignAccess.View` @@ -118,8 +122,6 @@ An **action menu item** appears on the target entity's data grid row (e.g., a "V An **action menu item** appears on the target entity's data grid row (e.g., an "Orders" item on the Customer row). Clicking it opens a fully functional CRUD modal where users can create, edit, and delete related records. -![Foreign access modal with full CRUD capabilities](images/foreign-access-modal.png) - ### `ForeignAccess.None` No action menu item is added. The foreign key exists only for data integrity and lookup display. @@ -143,6 +145,6 @@ The `DynamicEntityAppService` checks these relations when building entity action ## See Also -* [model.json Structure](model-json.md) +* [Model Descriptor Files](model-json.md) * [Reference Entities](reference-entities.md) * [Attributes & Fluent API](fluent-api.md) diff --git a/docs/en/low-code/images/actions-menu.png b/docs/en/low-code/images/actions-menu.png deleted file mode 100644 index b953394215..0000000000 Binary files a/docs/en/low-code/images/actions-menu.png and /dev/null differ diff --git a/docs/en/low-code/images/create-modal.png b/docs/en/low-code/images/create-modal.png deleted file mode 100644 index ebc77debb5..0000000000 Binary files a/docs/en/low-code/images/create-modal.png and /dev/null differ diff --git a/docs/en/low-code/images/data-grid.png b/docs/en/low-code/images/data-grid.png deleted file mode 100644 index 2a97d9ca6f..0000000000 Binary files a/docs/en/low-code/images/data-grid.png and /dev/null differ diff --git a/docs/en/low-code/images/designer-entity.png b/docs/en/low-code/images/designer-entity.png new file mode 100644 index 0000000000..f321f0f4a9 Binary files /dev/null and b/docs/en/low-code/images/designer-entity.png differ diff --git a/docs/en/low-code/images/designer-forms.png b/docs/en/low-code/images/designer-forms.png new file mode 100644 index 0000000000..230c7c6982 Binary files /dev/null and b/docs/en/low-code/images/designer-forms.png differ diff --git a/docs/en/low-code/images/designer-overview.png b/docs/en/low-code/images/designer-overview.png new file mode 100644 index 0000000000..f75ced1479 Binary files /dev/null and b/docs/en/low-code/images/designer-overview.png differ diff --git a/docs/en/low-code/images/designer-page-filters.png b/docs/en/low-code/images/designer-page-filters.png new file mode 100644 index 0000000000..647df1e064 Binary files /dev/null and b/docs/en/low-code/images/designer-page-filters.png differ diff --git a/docs/en/low-code/images/designer-properties.png b/docs/en/low-code/images/designer-properties.png new file mode 100644 index 0000000000..8d9cdf8569 Binary files /dev/null and b/docs/en/low-code/images/designer-properties.png differ diff --git a/docs/en/low-code/images/designer-relations.png b/docs/en/low-code/images/designer-relations.png new file mode 100644 index 0000000000..07e8aadfdb Binary files /dev/null and b/docs/en/low-code/images/designer-relations.png differ diff --git a/docs/en/low-code/images/foreign-access-modal.png b/docs/en/low-code/images/foreign-access-modal.png deleted file mode 100644 index b7a9c3189e..0000000000 Binary files a/docs/en/low-code/images/foreign-access-modal.png and /dev/null differ diff --git a/docs/en/low-code/images/menu-items.png b/docs/en/low-code/images/menu-items.png deleted file mode 100644 index d2fd84b009..0000000000 Binary files a/docs/en/low-code/images/menu-items.png and /dev/null differ diff --git a/docs/en/low-code/images/runtime-create-form.png b/docs/en/low-code/images/runtime-create-form.png new file mode 100644 index 0000000000..258bab6370 Binary files /dev/null and b/docs/en/low-code/images/runtime-create-form.png differ diff --git a/docs/en/low-code/images/runtime-data-grid.png b/docs/en/low-code/images/runtime-data-grid.png new file mode 100644 index 0000000000..c47cc03014 Binary files /dev/null and b/docs/en/low-code/images/runtime-data-grid.png differ diff --git a/docs/en/low-code/images/runtime-filters-has-value.png b/docs/en/low-code/images/runtime-filters-has-value.png new file mode 100644 index 0000000000..dfc7b30788 Binary files /dev/null and b/docs/en/low-code/images/runtime-filters-has-value.png differ diff --git a/docs/en/low-code/images/runtime-filters.png b/docs/en/low-code/images/runtime-filters.png new file mode 100644 index 0000000000..e63b8dcbea Binary files /dev/null and b/docs/en/low-code/images/runtime-filters.png differ diff --git a/docs/en/low-code/index.md b/docs/en/low-code/index.md index 7b5acfebe1..0960303ddb 100644 --- a/docs/en/low-code/index.md +++ b/docs/en/low-code/index.md @@ -3,363 +3,171 @@ ```json //[doc-seo] { - "Description": "ABP Low-Code System: Build admin panels with auto-generated CRUD UI, APIs, and permissions using C# attributes and Fluent API. No boilerplate code needed." + "Description": "ABP Low-Code System: design dynamic entities, forms, pages, permissions, menus, filters, and React runtime pages with the Admin Console Low-Code Designer." } ``` > You must have an ABP Team or a higher license to use this module. -The ABP Low-Code System allows you to define entities using C# attributes or Fluent API and automatically generates: +> **Preview:** The Low-Code System is currently in preview. APIs, designer behavior, generated metadata, and React runtime details may change before general availability. Use it for evaluation and controlled projects, and review release notes before upgrading. -* **Database tables** (via EF Core migrations) -* **CRUD REST APIs** (Get, GetList, Create, Update, Delete) -* **Permissions** (View, Create, Update, Delete per entity) -* **Menu items** (auto-added to the admin sidebar) -* **Full Blazor UI** (data grid, create/edit modals, filters, foreign key lookups) +The ABP Low-Code System lets you build data-driven admin screens from metadata. The primary workflow is the **Low-Code Designer** in ABP Admin Console, backed by the **React runtime** in your application. -No need to write DTOs, application services, repositories, or UI pages manually. +Use the designer to model entities, enums, properties, relations, pages, forms, filters, permissions, actions, and health checks. The runtime uses the same metadata to provide: -![Auto-generated menu items in the sidebar](images/menu-items.png) +* CRUD REST APIs +* EF Core dynamic entity tables +* Permission definitions +* Dynamic menu items +* React data grid, kanban, calendar, gallery, form, and dashboard pages +* Create and edit forms +* Advanced filters +* Excel, CSV, and file bundle export -## Why Low-Code? +No DTO, repository, application service, controller, or React CRUD page is required for the standard flow. -Traditionally, adding a new entity with full CRUD functionality to an ABP application requires: +![Low-Code Designer overview](images/designer-overview.png) -* Entity class in Domain -* DbContext configuration in EF Core -* DTOs in Application.Contracts -* AppService in Application -* Controller in HttpApi -* Razor/Blazor pages in UI -* Permissions, menu items, localization +## Supported UI -**With Low-Code, a single C# class replaces all of the above:** +Low-Code runtime UI is currently documented for **React**. The backend model, APIs, permissions, scripting, and custom endpoint infrastructure are shared by the module, but the UI walkthroughs in this section focus on Admin Console plus React. -````csharp -[DynamicEntity(DefaultDisplayPropertyName = "Name")] -[DynamicEntityUI(PageTitle = "Products")] -public class Product : DynamicEntityBase -{ - [DynamicPropertyUnique] - public string Name { get; set; } - - [DynamicPropertyUI(DisplayName = "Unit Price")] - public decimal Price { get; set; } - - public int StockCount { get; set; } - - [DynamicForeignKey("MyApp.Categories.Category", "Name")] - public Guid? CategoryId { get; set; } -} -```` - -Run `dotnet ef migrations add Added_Product` and start your application. You get a complete Product management page with search, filtering, sorting, pagination, create/edit forms, and foreign key dropdown — all auto-generated. +## How to Enable -![Auto-generated data grid with search, filters, and actions](images/data-grid.png) +The Low-Code System is an optional startup template feature. When creating a new application with [ABP Studio](../studio/index.md), choose a modern React application template and enable **Low-Code System** in the project creation wizard. -![Auto-generated create/edit modal with form fields and foreign key lookups](images/create-modal.png) +ABP Studio creates the required backend module references, dynamic model initializer, EF Core configuration, Admin Console integration, and React runtime wiring. -## Getting Started +The generated React project includes: -### 1. Create a Low-Code Initializer +* `@volo/abp-react-lowcode` +* `configureLowCode` +* `LowCodeLocalizationProvider` +* `createDynamicRoutes` +* `useMenuItems` +* Page, form, dashboard, file, and attachment hooks -Create a static initializer class in your Domain project's `_Dynamic` folder that registers your assembly and calls `DynamicModelManager.Instance.InitializeAsync()`: +The host application wires the low-code modules, calls the generated `_Dynamic` initializer, configures EF Core dynamic entities, and seeds the required OpenIddict clients. -````csharp -using Volo.Abp.Identity; -using Volo.Abp.LowCode.Configuration; -using Volo.Abp.LowCode.Modeling; -using Volo.Abp.Threading; +## Run the Application -namespace MyApp._Dynamic; - -public static class MyAppLowCodeInitializer -{ - private static readonly AsyncOneTimeRunner Runner = new(); - - public static async Task InitializeAsync() - { - await Runner.RunAsync(async () => - { - // Register reference entities (optional — for linking to existing C# entities) - AbpDynamicEntityConfig.ReferencedEntityList.Add( - nameof(IdentityUser.UserName), - nameof(IdentityUser.Email) - ); - - // Register assemblies containing [DynamicEntity] classes and model.json - var sourcePath = ResolveDomainSourcePath(); - AbpDynamicEntityConfig.SourceAssemblies.Add( - new DynamicEntityAssemblyInfo( - typeof(MyAppDomainModule).Assembly, - rootNamespace: "MyApp", - projectRootPath: sourcePath // Required for model.json hot-reload in development - ) - ); - - // Fluent API configurations (optional — highest priority) - AbpDynamicEntityConfig.EntityConfigurations.Configure("MyApp.Products.Product", entity => - { - entity.AddOrGetProperty("InternalNotes").AsServerOnly(); - }); - - // Initialize the dynamic model manager - await DynamicModelManager.Instance.InitializeAsync(); - }); - } - - private static string ResolveDomainSourcePath() - { - // Traverse up from bin folder to find the Domain project source - var baseDir = AppContext.BaseDirectory; - var current = new DirectoryInfo(baseDir); - - for (int i = 0; i < 10 && current != null; i++) - { - var candidate = Path.Combine(current.FullName, "src", "MyApp.Domain"); - if (Directory.Exists(Path.Combine(candidate, "_Dynamic"))) - { - return candidate; - } - current = current.Parent; - } - - // Fallback for production (embedded resource will be used instead) - return string.Empty; - } -} -```` - -> The `projectRootPath` parameter enables hot-reload of `model.json` during development. When the path is empty or the file doesn't exist, the module falls back to reading `model.json` as an embedded resource. - -### 2. Call the Initializer in Program.cs - -The initializer must be called **before** the application starts. Add it to `Program.cs`: - -````csharp -public static async Task Main(string[] args) -{ - // Initialize Low-Code before building the application - await MyAppLowCodeInitializer.InitializeAsync(); - - var builder = WebApplication.CreateBuilder(args); - // ... rest of your startup code -} -```` +After ABP Studio creates the solution, use **Solution Runner** to run the backend host and the React application. Run the database migration task before opening the runtime pages. -> **Important:** The initializer must also be called in your `DbMigrator` project and any other entry points (AuthServer, HttpApi.Host, etc.) that use dynamic entities. This ensures EF Core migrations can discover the entity schema. +The generated solution README contains the exact command-line equivalents if you prefer to run the projects outside ABP Studio. -### 3. Configure DbContext +If you generate a solution inside another repository, make sure parent build files such as `Directory.Packages.props` are not inherited accidentally. Use an empty output folder outside another solution, or isolate the generated solution's MSBuild configuration before running `dotnet build`. -Call `ConfigureDynamicEntities()` in your `DbContext`: +Open Admin Console and navigate to **Low-Code Designer** after the backend is running: -````csharp -protected override void OnModelCreating(ModelBuilder builder) -{ - builder.ConfigureDynamicEntities(); - base.OnModelCreating(builder); -} -```` - -### 3. Define Your First Entity - -````csharp -[DynamicEntity] -[DynamicEntityUI(PageTitle = "Customers")] -public class Customer : DynamicEntityBase -{ - public string Name { get; set; } - - [DynamicPropertyUI(DisplayName = "Phone Number")] - public string Telephone { get; set; } - - [DynamicForeignKey("Volo.Abp.Identity.IdentityUser", "UserName")] - public Guid? UserId { get; set; } -} -```` +```text +https://localhost:/admin-console/lowcode-designer +``` -### 4. Add Migration and Run +Open generated runtime pages after the React application is running: -```bash -dotnet ef migrations add Added_Customer -dotnet ef database update +```text +http://localhost:/dynamic/ ``` -Start your application — the Customer page is ready. +## Designer Workflow -## Two Ways to Define Entities +The designer is the day-to-day entry point. -### C# Attributes (Recommended) +1. Use **Data** to create entities, enums, properties, and relations. +2. Use **Pages** to choose a page type, menu placement, fields, default sorting, filters, dashboards, and linked forms. +3. Use **Forms** to arrange create and edit forms with tabs, groups, controls, validations, and actions. +4. Use **Permissions** to review generated permissions and control access. +5. Use **Actions** and **Interceptors** when the standard CRUD flow needs custom logic, endpoints, event handlers, jobs, or workers. +6. Use **Health** to review model issues before publishing changes. -Define entities as C# classes with attributes. You get compile-time checking, IntelliSense, and refactoring support: +![Entity properties in the designer](images/designer-properties.png) -````csharp -[DynamicEntity] -[DynamicEntityUI(PageTitle = "Orders")] -public class Order : DynamicEntityBase -{ - [DynamicForeignKey("MyApp.Customers.Customer", "Name", ForeignAccess.Edit)] - public Guid CustomerId { get; set; } +![Form setup in the designer](images/designer-forms.png) - public decimal TotalAmount { get; set; } - public bool IsDelivered { get; set; } -} +## React Runtime -[DynamicEntity(Parent = "MyApp.Orders.Order")] -public class OrderLine : DynamicEntityBase -{ - [DynamicForeignKey("MyApp.Products.Product", "Name")] - public Guid ProductId { get; set; } +React runtime pages are generated from the same metadata. The page below was produced from a low-code page definition and includes the grid, menu item, permissions, display values, export, create form, and filters. The same runtime can render kanban, calendar, gallery, standalone form, and dashboard page definitions. - public int Quantity { get; set; } - public decimal Amount { get; set; } -} -```` +![Generated React data grid](images/runtime-data-grid.png) -See [Attributes & Fluent API](fluent-api.md) for the full attribute reference. +![Generated React advanced filters](images/runtime-filters.png) -### model.json (Declarative) +![Generated React create form](images/runtime-create-form.png) -Alternatively, define entities in a JSON file without writing C# classes: +## Filters -```json -{ - "entities": [ - { - "name": "MyApp.Customers.Customer", - "displayProperty": "Name", - "properties": [ - { "name": "Name", "isRequired": true }, - { "name": "Telephone", "ui": { "displayName": "Phone Number" } } - ], - "ui": { "pageTitle": "Customers" } - } - ] -} -``` +React low-code filters are type-aware. The runtime shows only operators that make sense for the field type. For example: -See [model.json Structure](model-json.md) for the full specification. +* Text fields support contains, equals, starts with, ends with, and has value. +* Numeric fields support equals, comparison, between, and has value. +* Date fields use date-friendly labels such as on, after, before, and between. +* Boolean fields use an `All / Yes / No` value selector. +* File and image fields use `Has value` with an `All / Yes / No` value selector. -> Both approaches can be combined. The [three-layer configuration system](fluent-api.md#three-layer-configuration-system) merges Attributes, JSON, and Fluent API with clear priority rules. +`All` means no filter is applied. `Yes` maps to non-empty values. `No` maps to empty values. -## Key Features +![Has value filter options](images/runtime-filters-has-value.png) -| Feature | Description | Documentation | -|---------|-------------|---------------| -| **Attributes & Fluent API** | Define dynamic entities with C# attributes and configure programmatically | [Attributes & Fluent API](fluent-api.md) | -| **model.json** | Declarative dynamic entity definitions in JSON | [model.json Structure](model-json.md) | -| **Reference Entities** | Read-only access to existing C# entities (e.g., `IdentityUser`) for foreign key lookups | [Reference Entities](reference-entities.md) | -| **Interceptors** | Pre/Post hooks for Create, Update, Delete with JavaScript | [Interceptors](interceptors.md) | -| **Scripting API** | Server-side JavaScript for database queries and CRUD | [Scripting API](scripting-api.md) | -| **Custom Endpoints** | REST APIs with JavaScript handlers | [Custom Endpoints](custom-endpoints.md) | -| **Foreign Access** | View/Edit related dynamic entities from the target entity's UI | [Foreign Access](foreign-access.md) | -| **Export** | Export dynamic entity data to Excel (XLSX) or CSV | See below | +## Export -## Export (Excel / CSV) +Every dynamic entity page can export data to Excel or CSV. Pages with file or image fields can also export a file bundle as a ZIP. Export requests use the current search, sorting, and filters from the runtime view, so a filtered page exports the matching subset instead of the whole entity. -The Low-Code System provides built-in export functionality for all dynamic entities. Users can export filtered data to **Excel (XLSX)** or **CSV** directly from the Blazor UI. +The React runtime exports visible exportable columns by default. These columns and their default order come from the page-level **Export Fields** settings in the Low-Code Designer. A field can be visible but not exportable, or hidden but still available in the **All exportable fields** option. Use this when a page should display operational data that should not leave the system through Excel or CSV. Server-only fields are always excluded, and foreign key values are displayed through their configured display property. -### How It Works +File and image fields are exported as file names by default. Export options can expand those fields into metadata columns or temporary download-link columns. Download-link columns include file name, URL, expiry, content type, size, dimensions, and status. The links are short-lived and should be treated like signed download links, not permanent public URLs. -1. The client calls `GET /api/low-code/entities/{entityName}/download-token` to obtain a single-use download token (valid for 30 seconds). -2. The client calls `GET /api/low-code/entities/{entityName}/export-as-excel` or `GET /api/low-code/entities/{entityName}/export-as-csv` with the token and optional filters. +Use **Files (.zip)** when users need the actual uploaded files. The action appears only when the Designer allows file bundle export and at least one selected file/image field is exportable. The ZIP contains `manifest.csv` and files under `files/{recordId}/{fieldName}/{safeFileName}`. The manifest records missing, malformed, unlinked, and limit-skipped files instead of failing the whole export. -### API Endpoints +Spreadsheet and ZIP exports require a short-lived, single-use download token. The token is bound to the tenant, page, entity, child page, and foreign-access context. File download links use separate short-lived tokens bound to the exported file value. Spreadsheet formula-like text values are escaped before writing CSV or Excel headers and cells. | Endpoint | Description | |----------|-------------| -| `GET /api/low-code/entities/{entityName}/download-token` | Get a single-use download token | -| `GET /api/low-code/entities/{entityName}/export-as-excel` | Export as Excel (.xlsx) | -| `GET /api/low-code/entities/{entityName}/export-as-csv` | Export as CSV (.csv) | - -Export requests accept the same filtering, sorting, and search parameters as the list endpoint. Server-only properties are automatically excluded, and foreign key columns display the referenced entity's display value instead of the raw ID. - -## Custom Commands and Queries - -The Low-Code System allows you to replace or extend the default CRUD operations by implementing custom command and query handlers in C#. - -### Custom Commands - -Create a class that implements `ILcCommand` and decorate it with `[CustomCommand]`: - -````csharp -[CustomCommand("Create", "MyApp.Products.Product")] -public class CustomProductCreateCommand : CreateCommand -{ - public override async Task ExecuteWithResultAsync(DynamicCommandArgs commandArgs) - { - // Your custom create logic here - // ... - } -} -```` - -| Parameter | Description | -|-----------|-------------| -| `commandName` | The command to replace: `"Create"`, `"Update"`, or `"Delete"` | -| `entityName` | Full entity name (e.g., `"MyApp.Products.Product"`) | - -### Custom Queries - -Create a class that implements `ILcQuery` and decorate it with `[CustomQuery]`: - -````csharp -[CustomQuery("List", "MyApp.Products.Product")] -public class CustomProductListQuery : ILcQuery -{ - public async Task ExecuteAsync(DynamicQueryArgs queryArgs) - { - // Your custom list query logic here - // ... - } -} -```` - -````csharp -[CustomQuery("Single", "MyApp.Products.Product")] -public class CustomProductListQuery : ILcQuery -{ - public async Task ExecuteAsync(DynamicQueryArgs queryArgs) - { - // Your custom single query logic here - // ... - } -} -```` - -| Parameter | Description | -|-----------|-------------| -| `queryName` | The query to replace: `"List"` or `"Single"` | -| `entityName` | Full entity name (e.g., `"MyApp.Products.Product"`) | - -Custom commands and queries are automatically discovered and registered at startup. They completely replace the default handler for the specified entity and operation. - -## Internals - -### Domain Layer - -* `DynamicModelManager`: Singleton managing all entity metadata with a layered configuration architecture (Code > JSON > Fluent > Defaults). -* `EntityDescriptor`: Entity definition with properties, foreign keys, interceptors, and UI configuration. -* `EntityPropertyDescriptor`: Property definition with type, validation, UI settings, and foreign key info. -* `IDynamicEntityRepository`: Repository for dynamic entity CRUD operations. - -### Application Layer - -* `DynamicEntityAppService`: CRUD operations for all dynamic entities (Get, GetList, Create, Update, Delete, Export). -* `DynamicEntityUIAppService`: UI definitions, menu items, and page configurations. Provides: - * `GetUiDefinitionAsync(entityName)` — Full UI definition (filters, columns, forms, children, foreign access actions, permissions) - * `GetUiCreationFormDefinitionAsync(entityName)` — Creation form fields with validation rules - * `GetUiEditFormDefinitionAsync(entityName)` — Edit form fields with validation rules - * `GetMenuItemsAsync()` — Menu items for all entities that have a `pageTitle` configured (filtered by permissions) -* `DynamicPermissionDefinitionProvider`: Auto-generates permissions per entity. -* `CustomEndpointExecutor`: Executes JavaScript-based custom endpoints. - -### Database Providers - -**Entity Framework Core**: Dynamic entities are configured as EF Core [shared-type entities](https://learn.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=fluent-api#shared-type-entity-types) via the `ConfigureDynamicEntities()` extension method. +| `GET /api/low-code/pages/{pageName}/download-token` | Gets a short-lived download token | +| `GET /api/low-code/pages/{pageName}/export/excel` | Exports filtered data as Excel | +| `GET /api/low-code/pages/{pageName}/export/csv` | Exports filtered data as CSV | +| `GET /api/low-code/pages/{pageName}/export/files` | Exports selected file/image fields as a ZIP bundle | +| `GET /api/low-code/pages/export/files/{token}` | Downloads one temporary file link created by spreadsheet export | + +Useful export settings: + +| Setting | Default | Purpose | +|---------|---------|---------| +| `LowCode:Export:MaxRows` | `100000` | Maximum rows in one all-filtered export | +| `LowCode:Export:DownloadTokenLifetimeSeconds` | `30` | Download token lifetime | +| `LowCode:Export:FileLinkTokenLifetimeSeconds` | `900` | Temporary file link lifetime | +| `LowCode:Export:MaxFileBundleFiles` | `1000` | Maximum files in one ZIP export | +| `LowCode:Export:MaxFileBundleBytes` | `268435456` | Maximum total file bytes in one ZIP export | + +## Advanced Configuration + +The designer stores and reads the same descriptor metadata described in the reference pages below. Use these pages when you need source-controlled descriptor files, custom startup wiring, script handlers, or low-level integration details. + +| Topic | Use it for | +|-------|------------| +| [Designer](designer.md) | Admin Console tabs, entity/page/form setup, permissions, and health | +| [React Runtime](react-runtime.md) | React package wiring, routes, menu items, filters, forms, and export | +| [Attributes & Fluent API](fluent-api.md) | Source-controlled C# metadata and runtime overrides | +| [Model Descriptor Files](model-json.md) | JSON descriptor files and public descriptor schemas used by the designer and runtime | +| [Reference Entities](reference-entities.md) | Lookups to existing entities such as Identity users | +| [Foreign Access](foreign-access.md) | Access to related dynamic entities through relations | +| [Interceptors](interceptors.md) | JavaScript lifecycle logic for CRUD operations | +| [Custom Endpoints](custom-endpoints.md) | JavaScript-backed REST endpoints | +| [Script Actions](script-actions.md) | Event handlers, background jobs, background workers, script editor, and dry-run testing | +| [Scripting API](scripting-api.md) | Server-side script context and helpers | + +## Runtime Internals + +The generated pages are powered by these services: + +* `DynamicEntityAppService` handles CRUD, list queries, filtering, sorting, and export. +* `DynamicPageAppService` exposes page-based CRUD, file, attachment, lookup, child, foreign-access, and export endpoints. +* `DynamicEntityUIAppService` returns page, form, dashboard, field, filter, and menu metadata. +* `DynamicPermissionDefinitionProvider` creates permissions for dynamic entities. +* `CustomEndpointExecutor` runs JavaScript-backed custom endpoints. +* EF Core maps dynamic entities as shared-type entities. ## See Also -* [Attributes & Fluent API](fluent-api.md) -* [model.json Structure](model-json.md) -* [Scripting API](scripting-api.md) +* [Low-Code Designer](designer.md) +* [React Runtime](react-runtime.md) +* [Model Descriptor Files](model-json.md) diff --git a/docs/en/low-code/interceptors.md b/docs/en/low-code/interceptors.md index 7eaf97eb77..a8f1b06942 100644 --- a/docs/en/low-code/interceptors.md +++ b/docs/en/low-code/interceptors.md @@ -7,6 +7,10 @@ # Interceptors +> **Preview:** Interceptors and their JavaScript context are preview extension points. Script context members, validation behavior, and lifecycle hooks may change before general availability. + +Use designer actions and descriptor metadata for standard low-code behavior first. Interceptors are an advanced extension point for adding JavaScript lifecycle logic when the generated CRUD flow needs validation, transformation, or replacement behavior. + Interceptors allow you to run custom JavaScript code before, after, or instead of Create, Update, and Delete operations on dynamic entities. ## Interceptor Types @@ -23,6 +27,31 @@ Interceptors allow you to run custom JavaScript code before, after, or instead o | `Delete` | `Post` | After entity deletion — cleanup | | `Delete` | `Replace` | Instead of entity deletion — no return value needed | +## Defining Interceptors in JSON Descriptors + +The designer stores entity interceptors in the entity `interceptors` array: + +```json +{ + "name": "LowCodeDemo.Customers.Customer", + "interceptors": [ + { + "commandName": "Create", + "type": "Pre", + "javascript": "if (!args.getValue('Name')) {\n globalError = 'Name is required.';\n}" + } + ] +} +``` + +### Interceptor Descriptor + +| Field | Type | Description | +|-------|------|-------------| +| `commandName` | string | `"Create"`, `"Update"`, or `"Delete"` | +| `type` | string | `"Pre"`, `"Post"`, or `"Replace"` | +| `javascript` | string | JavaScript code to execute | + ## Defining Interceptors with Attributes Use the `[DynamicEntityCommandInterceptor]` attribute on a C# class: @@ -49,7 +78,7 @@ The `Name` parameter must be one of: `"Create"`, `"Update"`, or `"Delete"`. The ## Defining Interceptors with Fluent API -Use the `Interceptors` list on an `EntityDescriptor` to add interceptors programmatically in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer): +Use the `Interceptors` list on an `EntityDescriptor` to add interceptors programmatically in startup configuration: ````csharp AbpDynamicEntityConfig.EntityConfigurations.Configure( @@ -71,32 +100,7 @@ AbpDynamicEntityConfig.EntityConfigurations.Configure( ); ```` -See [Attributes & Fluent API](fluent-api.md#adding-interceptors) for more details on Fluent API configuration. - -## Defining Interceptors in model.json - -Add interceptors to the `interceptors` array of an entity: - -```json -{ - "name": "LowCodeDemo.Customers.Customer", - "interceptors": [ - { - "commandName": "Create", - "type": "Pre", - "javascript": "if(context.commandArgs.data['Name'] == 'Invalid') {\n globalError = 'Invalid Customer Name!';\n}" - } - ] -} -``` - -### Interceptor Descriptor - -| Field | Type | Description | -|-------|------|-------------| -| `commandName` | string | `"Create"`, `"Update"`, or `"Delete"` | -| `type` | string | `"Pre"`, `"Post"`, or `"Replace"` | -| `javascript` | string | JavaScript code to execute | +See [Attributes & Fluent API](fluent-api.md) for more details on Fluent API configuration. ## JavaScript Context @@ -138,7 +142,13 @@ Inside interceptor scripts, you have access to: |--------|-------------| | `isAvailable` | Whether the email sender is configured and available | | `sendAsync(to, subject, body)` | Send a plain-text email | +| `sendAsync(from, to, subject, body)` | Send a plain-text email with an explicit sender | | `sendHtmlAsync(to, subject, htmlBody)` | Send an HTML email | +| `sendHtmlAsync(from, to, subject, htmlBody)` | Send an HTML email with an explicit sender | +| `queueAsync(to, subject, body)` | Queue a plain-text email | +| `queueAsync(from, to, subject, body)` | Queue a plain-text email with an explicit sender | +| `queueHtmlAsync(to, subject, htmlBody)` | Queue an HTML email | +| `queueHtmlAsync(from, to, subject, htmlBody)` | Queue an HTML email with an explicit sender | ### Logging @@ -154,6 +164,8 @@ Inside interceptor scripts, you have access to: Full access to the [Scripting API](scripting-api.md) for querying and mutating data. +Interceptors can also use common scripting services such as `user`, `tenant`, `auth`, `settings`, `features`, `config`, `http`, `events`, `jobs`, `blob`, `encryption`, `textTemplating`, `files`, `images`, and `attachments` when they are enabled by the scripting capability profile. + ### `globalError` Set this variable to a string to **abort** the operation and return an error: @@ -245,5 +257,5 @@ When you need to completely replace the default create operation with custom log ## See Also * [Scripting API](scripting-api.md) -* [model.json Structure](model-json.md) +* [Model Descriptor Files](model-json.md) * [Custom Endpoints](custom-endpoints.md) diff --git a/docs/en/low-code/model-json.md b/docs/en/low-code/model-json.md index 2f58876a29..2c11503553 100644 --- a/docs/en/low-code/model-json.md +++ b/docs/en/low-code/model-json.md @@ -1,309 +1,417 @@ ```json //[doc-seo] { - "Description": "Define dynamic entities using model.json in the ABP Low-Code System. Learn about entity properties, enums, foreign keys, validators, UI configuration, and migration requirements." + "Description": "Define ABP Low-Code descriptor metadata and descriptor schemas for dynamic entities, pages, forms, filters, permissions, script endpoints, event handlers, background jobs, and workers." } ``` -# model.json Structure +# Model Descriptor Files -The `model.json` file defines all your dynamic entities, their properties, enums, relationships, interceptors, custom endpoints, and UI configurations. It is an alternative configuration source to [C# Attributes and Fluent API](fluent-api.md), ideal when you prefer a declarative JSON approach. +> **Preview:** The Low-Code System is currently in preview. The descriptor format is stable enough for evaluation and source control, but fields may change before general availability. + +Low-code metadata is source-controlled as JSON descriptor files used by the Low-Code Designer and React runtime. Current generated projects keep descriptors as split files under `_Dynamic`; older projects may still contain an aggregate `_Dynamic/model.json` document. Use the [Low-Code Designer](designer.md) for normal editing. Use this page when you need to review, generate, merge, or source-control the JSON metadata directly. ## File Location -Place your `model.json` in a `_Dynamic` folder inside your **Domain** project: +Generated low-code applications keep descriptor files in a `_Dynamic` folder under the application domain project. A typical project stores one JSON file per descriptor: -``` +```text YourApp.Domain/ -└── _Dynamic/ - └── model.json +`-- _Dynamic/ + |-- entities/ + | `-- Acme.Campaigns.Campaign.json + |-- pages/ + | `-- campaigns.json + |-- forms/ + | `-- campaign-form.json + `-- permissions/ + `-- Acme.Campaigns.json ``` -The module automatically discovers and loads this file at application startup. +Exact folders and file names are generated by the tooling for the descriptor type. Keep the whole `_Dynamic` folder and the generated initializer in source control. The low-code module discovers the descriptor metadata during application startup. + +## JSON Schemas + +ABP publishes JSON Schema definitions for the descriptor objects that make up low-code metadata. These schemas are useful when you generate descriptors, review changes in source control, or want IDE validation for split descriptor files. + +The schema files live in the ABP repository under `schemas/low-code`. Use the branch or tag that matches your ABP version. + +The schema manifest is published at: + +```text +https://raw.githubusercontent.com/abpframework/abp/rel-10.5/schemas/low-code/manifest.json +``` -> A JSON Schema file (`model.schema.json`) is available in the module source for IDE IntelliSense. Reference it using the `$schema` property: +For another version, replace `rel-10.5` with the matching ABP branch or tag. + +The manifest maps each descriptor collection to its descriptor schema: + +| Descriptor collection | Descriptor schema | +|-----------------------|-------------------| +| `enums` | `definitions/enum-descriptor.schema.json` | +| `entities` | `definitions/entity-descriptor.schema.json` | +| `endpoints` | `definitions/endpoint-descriptor.schema.json` | +| `eventHandlers` | `definitions/script-event-handler-descriptor.schema.json` | +| `backgroundJobs` | `definitions/script-background-job-descriptor.schema.json` | +| `backgroundWorkers` | `definitions/script-background-worker-descriptor.schema.json` | +| `pageGroups` | `definitions/page-group-descriptor.schema.json` | +| `pages` | `definitions/page-descriptor.schema.json` | +| `forms` | `definitions/form-descriptor.schema.json` | +| `permissions` | `definitions/permission-descriptor.schema.json` | + +### Split Descriptor Files + +Use the descriptor schema directly when a descriptor is stored as its own JSON file. The descriptor object is the same shape used for one item inside the related descriptor collection. ```json { - "$schema": "path/to/model.schema.json", - "entities": [] + "$schema": "https://raw.githubusercontent.com/abpframework/abp/rel-10.5/schemas/low-code/definitions/entity-descriptor.schema.json", + "name": "Acme.Campaigns.Campaign", + "displayName": "Campaigns", + "properties": [] } ``` -## Top-Level Structure +For example, a file that stores one page descriptor can reference `page-descriptor.schema.json`, and a file that stores one form descriptor can reference `form-descriptor.schema.json`. -The `model.json` file has three root sections: +The published schemas validate individual descriptor objects, not an aggregate descriptor document. If you still maintain an aggregate `_Dynamic/model.json`, do not point it at `entity-descriptor.schema.json`, `page-descriptor.schema.json`, or another descriptor schema; those schemas expect one descriptor object, not the top-level arrays. For aggregate metadata, use the manifest table above as the section-level reference and validate each array item with its matching descriptor schema. + +## Top-Level Sections + +When descriptor metadata is viewed as an aggregate document, the logical sections are page/form centered. Entities define data shape; pages and forms define the React runtime UI. ```json { - "$schema": "...", "enums": [], "entities": [], - "endpoints": [] + "endpoints": [], + "eventHandlers": [], + "backgroundJobs": [], + "backgroundWorkers": [], + "pageGroups": [], + "pages": [], + "forms": [], + "permissions": [] } ``` | Section | Description | |---------|-------------| -| `enums` | Enum type definitions | -| `entities` | Entity definitions with properties, foreign keys, interceptors, and UI | -| `endpoints` | Custom REST API endpoints with JavaScript handlers | +| `enums` | Reusable enum definitions | +| `entities` | Dynamic entities, properties, relations, attachments, validations, and interceptors | +| `endpoints` | JavaScript-backed custom HTTP endpoints | +| `eventHandlers` | JavaScript handlers for distributed events | +| `backgroundJobs` | Named JavaScript background job handlers | +| `backgroundWorkers` | Scheduled JavaScript workers | +| `pageGroups` | Menu folders used by runtime pages | +| `pages` | React runtime page definitions, including data grids, kanban, calendar, gallery, form pages, and dashboards | +| `forms` | Named form definitions referenced by pages | +| `permissions` | Custom permission definitions referenced by pages and endpoints | -## Enum Definitions +## Enums -Define enums that can be used as property types: +Define enums before properties that reference them: ```json { "enums": [ { - "name": "LowCodeDemo.Organizations.OrganizationType", + "name": "Acme.Campaigns.CampaignStatus", "values": [ - { "name": "Corporate", "value": 0 }, - { "name": "Enterprise", "value": 1 }, - { "name": "Startup", "value": 2 }, - { "name": "Consulting", "value": 3 } + { "name": "Draft", "value": 0 }, + { "name": "Active", "value": 1 }, + { "name": "Paused", "value": 2 }, + { "name": "Completed", "value": 3 } ] } ] } ``` -Reference enums in entity properties using the `enumType` field: +Use the enum from a property with `type: "enum"` and `enumType`: ```json { - "name": "OrganizationType", - "enumType": "LowCodeDemo.Organizations.OrganizationType" + "name": "Status", + "type": "enum", + "enumType": "Acme.Campaigns.CampaignStatus", + "defaultValue": "0" } ``` -## Entity Definition +## Entities -Each entity has the following structure: +Entities describe the persisted data model. UI is not configured with legacy property `ui` objects. Use page `columns` and `filters`, and named `forms`, for runtime UI behavior. ```json { - "name": "LowCodeDemo.Products.Product", + "name": "Acme.Campaigns.Campaign", + "displayName": "Campaigns", "displayProperty": "Name", - "parent": null, "properties": [], - "interceptors": [], - "ui": {} + "crossFieldValidations": [], + "interceptors": [] } ``` -### Entity Attributes - -| Attribute | Type | Description | -|-----------|------|-------------| -| `name` | string | **Required.** Full entity name with namespace (e.g., `"MyApp.Products.Product"`) | -| `displayProperty` | string | Property to display in lookups and foreign key dropdowns | -| `parent` | string | Parent entity name for parent-child (master-detail) relationships | -| `properties` | array | Property definitions | -| `interceptors` | array | CRUD lifecycle interceptors | -| `ui` | object | UI configuration | - -### Parent-Child Relationships - -Use the `parent` field to create nested entities. Children are managed through the parent entity's UI: - -```json -{ - "name": "LowCodeDemo.Orders.OrderLine", - "parent": "LowCodeDemo.Orders.Order", - "properties": [ - { - "name": "ProductId", - "foreignKey": { - "entityName": "LowCodeDemo.Products.Product" - } - }, - { "name": "Quantity", "type": "int" }, - { "name": "Amount", "type": "decimal" } - ] -} -``` - -Multi-level nesting is supported (e.g., `Order > OrderLine > ShipmentItem > ShipmentTracking`). +| Field | Description | +|-------|-------------| +| `name` | Required stable full entity name, for example `Acme.Campaigns.Campaign` | +| `displayName` | Default plural/screen label | +| `displayProperty` | Property shown in lookups and foreign key display values | +| `parent` | Parent entity name for child/detail entities | +| `attachments` | Record-level attachment settings | +| `properties` | Entity property definitions | +| `crossFieldValidations` | Validation rules comparing two properties | +| `interceptors` | Create, update, and delete lifecycle scripts | -## Property Definition +### Properties ```json { - "name": "Price", - "type": "decimal", + "name": "Budget", + "type": "money", "isRequired": true, "isUnique": false, - "isMappedToDbField": true, - "serverOnly": false, "allowSetByClients": true, - "enumType": null, - "foreignKey": null, - "validators": [], - "ui": {} + "serverOnly": false, + "isMappedToDbField": true, + "validators": [ + { "type": "range", "minimum": 0, "maximum": 1000000 } + ] } ``` +| Field | Description | +|-------|-------------| +| `name` | Required PascalCase property name | +| `type` | Property type; omitted means `string` | +| `displayName` | Default field label; pages/forms can override it | +| `enumType` | Enum name when `type` is `enum` | +| `defaultValue` | Default value for new records, stored as a string and converted at runtime | +| `isRequired` | Required/not nullable backend and UI validation | +| `isUnique` | Unique value validation | +| `serverOnly` | Hidden from clients, API responses, and UI metadata | +| `allowSetByClients` | Whether create/update clients may set this value | +| `isMappedToDbField` | Whether the property is stored in the database | +| `foreignKey` | Lookup relation metadata | +| `validators` | Backend/UI validation rules | + ### Property Types | Type | Description | |------|-------------| -| `string` | Text (default if type is omitted) | -| `int` | 32-bit integer | -| `long` | 64-bit integer | -| `decimal` | Decimal number | -| `DateTime` | Date and time | +| `string` | Text | +| `int`, `long` | Whole numbers | +| `decimal`, `money` | Decimal numbers and money values | +| `dateTime`, `date`, `time` | Date/time values | | `boolean` | True/false | -| `Guid` | GUID/UUID | -| `Enum` | Enum type (requires `enumType` field) | +| `guid` | GUID value | +| `enum` | Integer-backed enum; requires `enumType` | +| `file`, `image` | Upload metadata handled by the low-code file pipeline | -### Property Flags +### File, Image, and Attachments -| Flag | Type | Default | Description | -|------|------|---------|-------------| -| `isRequired` | bool | `false` | Property must have a value | -| `isUnique` | bool | `false` | Value must be unique across all records | -| `isMappedToDbField` | bool | `true` | Property is stored in the database | -| `serverOnly` | bool | `false` | Property is hidden from API clients | -| `allowSetByClients` | bool | `true` | Whether clients can set this value | +Use `file` or `image` properties for first-class upload fields: -### Foreign Key Properties +```json +{ + "name": "CoverImage", + "type": "image", + "fileAllowedContentTypes": ["image/*"], + "fileMaxSizeBytes": 5242880, + "imageMaxWidth": 1600, + "imageMaxHeight": 900, + "imageResizeMode": "fit" +} +``` -Define a foreign key relationship inline on a property: +Use entity `attachments` when each record can have multiple arbitrary files: ```json { - "name": "CustomerId", - "foreignKey": { - "entityName": "LowCodeDemo.Customers.Customer", - "displayPropertyName": "Name", - "access": "edit" + "name": "Acme.Campaigns.Campaign", + "attachments": { + "isEnabled": true, + "maxFileCount": 10, + "maxFileSizeBytes": 5242880, + "allowedContentTypes": ["application/pdf", "image/*"] } } ``` -| Attribute | Description | -|-----------|-------------| -| `entityName` | **Required.** Full name of the target entity — can be a **dynamic entity** (e.g., `"LowCodeDemo.Customers.Customer"`) or a **[reference entity](reference-entities.md)** (e.g., `"Volo.Abp.Identity.IdentityUser"`) | -| `displayPropertyName` | Property to display in lookups (defaults to entity's `displayProperty`) | -| `access` | [Foreign access](foreign-access.md) level: `"none"`, `"view"`, or `"edit"` | +### Foreign Keys + +```json +{ + "name": "OwnerId", + "type": "guid", + "foreignKey": { + "entityName": "Volo.Abp.Identity.IdentityUser", + "displayPropertyName": "UserName", + "access": "none" + } +} +``` -> **Note:** [Reference entities](reference-entities.md) are existing C# entities (like ABP's `IdentityUser`) that are registered for read-only access. Unlike dynamic entities, they don't get CRUD pages — they're used only for foreign key lookups and display values. +`entityName` can point to another dynamic entity or a registered [reference entity](reference-entities.md). `access` controls [Foreign Access](foreign-access.md) behavior for dynamic entity relations. ### Validators -Add validation rules to properties: - ```json { "name": "EmailAddress", + "type": "string", "validators": [ { "type": "required" }, - { "type": "emailAddress" }, - { "type": "minLength", "length": 5 }, + { "type": "email" }, { "type": "maxLength", "length": 255 } ] } ``` -Additional validator examples: +Common validators include `required`, `minLength`, `maxLength`, `stringLength`, `email`, `emailAddress`, `phone`, `url`, `creditCard`, `regularExpression`, `range`, `min`, and `max`. Validators can include a custom `message`. + +## Pages + +Pages create runtime routes and menu entries. They also choose how entity data is rendered in React. ```json { - "name": "Website", - "validators": [ - { "type": "url", "message": "Please enter a valid URL" } - ] -}, -{ - "name": "PhoneNumber", - "validators": [ - { "type": "phone" } - ] -}, -{ - "name": "ProductCode", - "validators": [ - { "type": "regularExpression", "pattern": "^[A-Z]{3}-\\d{4}$", "message": "Code must be in format ABC-1234" } - ] -}, -{ - "name": "Price", - "type": "decimal", - "validators": [ - { "type": "range", "minimum": 0.01, "maximum": 99999.99 } - ] + "name": "campaigns", + "title": "Campaigns", + "icon": "fa-solid fa-bullhorn", + "type": "dataGrid", + "entityName": "Acme.Campaigns.Campaign", + "group": "marketing", + "defaultFileExportMode": 0, + "allowFileBundleExport": true, + "columns": [ + { "propertyName": "Name", "order": 0, "exportOrder": 0 }, + { "propertyName": "Status", "order": 1, "exportOrder": 1 }, + { "propertyName": "Budget", "order": 2, "exportOrder": 2, "exportable": false } + ], + "filters": [ + { "propertyName": "Name", "control": "text", "defaultOperator": "contains" }, + { "propertyName": "Status", "control": "select", "defaultOperator": "equal" } + ], + "createFormName": "campaign-form", + "editFormName": "campaign-form" } ``` -| Validator | Parameters | Applies To | Description | -|-----------|------------|------------|-------------| -| `required` | `allowEmptyStrings` (optional) | All types | Value is required | -| `minLength` | `length` | String | Minimum string length | -| `maxLength` | `length` | String | Maximum string length | -| `stringLength` | `minimumLength`, `maximumLength` | String | String length range (min and max together) | -| `emailAddress` | — | String | Must be a valid email | -| `phone` | — | String | Must be a valid phone number | -| `url` | — | String | Must be a valid URL | -| `creditCard` | — | String | Must be a valid credit card number | -| `regularExpression` | `pattern` | String | Must match the regex pattern | -| `range` | `minimum`, `maximum` | Numeric | Numeric range | -| `min` | `minimum` | Numeric | Minimum numeric value | -| `max` | `maximum` | Numeric | Maximum numeric value | +Page columns support two independent flags: + +| Field | Default | Purpose | +|-------|---------|---------| +| `visible` | `true` | Renders the field in the React page view | +| `exportOrder` | `order` | Optional page-level export order. Lower values are exported first | +| `exportable` | `true` | Page-level export flag managed by **Export Fields**. Allows the field to be included in Excel, CSV, download-link columns, and file bundle export | + +If `columns` is present, export uses this list as the page-level export policy. `exportable: false` prevents the field from being exported even if a caller sends the field name manually. `exportOrder` controls default export order without changing display order. Server-only entity properties are never exportable. -> All validators accept an optional `message` parameter for a custom error message. The `regularExpression` validator also accepts the alias `pattern`, and `emailAddress` also accepts `email`. +Page export settings: -## UI Configuration +| Field | Default | Purpose | +|-------|---------|---------| +| `defaultFileExportMode` | `0` | Default spreadsheet output for file/image fields. `0` = file name, `1` = metadata columns, `2` = temporary download-link columns | +| `allowFileBundleExport` | `true` | Allows **Files (.zip)** export for exportable file/image columns on the page | -### Entity-Level UI +ZIP file bundle export only includes selected page columns that are file or image fields and are exportable. The ZIP contains `manifest.csv` plus files under `files/{recordId}/{fieldName}/{safeFileName}`. + +| Page type | Required fields | Purpose | +|-----------|-----------------|---------| +| `dataGrid` | `entityName` | Searchable, sortable CRUD grid | +| `kanban` | `entityName`, `groupByProperty` | Cards grouped by an enum/status-like property | +| `calendar` | `entityName`, `calendarStartProperty` | Records shown on a calendar | +| `gallery` | `entityName` | Visual/card list, optionally using `galleryImageProperty` | +| `form` | `entityName`, `formName` | Standalone form page | +| `dashboard` | `dashboard` | Dashboard visualizations | + +Runtime routes use the page name: + +```text +/dynamic/ +/dynamic//create +/dynamic//edit/ +/dynamic// +``` + +## Forms + +Forms are named definitions referenced by pages through `formName`, `createFormName`, or `editFormName`. ```json { - "ui": { - "pageTitle": "Products" + "name": "campaign-form", + "entityName": "Acme.Campaigns.Campaign", + "enableSaveAndNew": true, + "fields": [ + { "id": "name", "label": "Name", "type": "text", "binding": "Name" }, + { "id": "status", "label": "Status", "type": "select", "binding": "Status", "enumType": "Acme.Campaigns.CampaignStatus" }, + { "id": "ownerId", "label": "Owner", "type": "lookup", "binding": "OwnerId" } + ], + "layout": { + "tabs": [ + { + "id": "main", + "title": "Main", + "isDefault": true, + "groups": [ + { + "id": "details", + "title": "Details", + "isDefault": true, + "fields": [ + { "fieldId": "name", "row": 0, "colSpan": 4 }, + { "fieldId": "status", "row": 1, "colSpan": 2 }, + { "fieldId": "ownerId", "row": 1, "colSpan": 2 } + ] + } + ] + } + ] } } ``` -> Only entities with `ui.pageTitle` get a menu item and a dedicated page in the UI. +Form fields can be `text`, `textarea`, `number`, `checkbox`, `date`, `datetime`, `time`, `file`, `image`, `money`, `select`, `lookup`, `guid`, or `computed`. Form rules can hide, show, disable, enable, or set values for fields/groups. + +## Filters + +Filters are page-owned. Use `control: "auto"` unless you need a specific control. + +| Property type | Typical operators | +|---------------|-------------------| +| `string` | `contains`, `equal`, `notEqual`, `startsWith`, `endsWith`, `notContains`, `hasValue` | +| `int`, `long`, `decimal`, `money` | `between`, `equal`, `notEqual`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`, `hasValue` | +| `date`, `dateTime`, `time` | `between`, `equal`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`, `hasValue` | +| `boolean` | `All / Yes / No` value selector | +| `enum`, lookup, `guid` | `equal`, `notEqual`, `in`, `notIn`, `hasValue` | +| `file`, `image` | `hasValue` with `All / Yes / No` | + +`hasValue` is a UI alias. At runtime, `Yes` maps to `IsNotNull`, `No` maps to `IsNull`, and `All` does not add a filter. + +## Permissions -### Property-Level UI +Pages can use generated defaults or explicit permission configuration: ```json { - "name": "RegistrationNumber", - "ui": { - "displayName": "Registration Number", - "isAvailableOnDataTable": true, - "isAvailableOnDataTableFiltering": true, - "creationFormAvailability": "Hidden", - "editingFormAvailability": "NotAvailable", - "quickLookOrder": 100 + "permissionConfig": { + "view": "authenticated", + "create": "Acme.Campaigns.Create", + "update": "Acme.Campaigns.Update", + "delete": "Acme.Campaigns.Delete" } } ``` -| Attribute | Type | Default | Description | -|-----------|------|---------|-------------| -| `displayName` | string | Property name | Display label in UI | -| `isAvailableOnDataTable` | bool | `true` | Show in data grid | -| `isAvailableOnDataTableFiltering` | bool | `true` | Show in filter panel | -| `creationFormAvailability` | string | `"Available"` | Visibility in create form | -| `editingFormAvailability` | string | `"Available"` | Visibility in edit form | -| `quickLookOrder` | int | -2 | Order in quick-look panel (-2 = not shown) | +Custom permission definitions live in the top-level `permissions` section and can be granted through the normal ABP permission management UI. -#### Form Availability Values +## Scripts -| Value | Description | -|-------|-------------| -| `Available` | Visible and editable | -| `Hidden` | Not visible in the form | -| `NotAvailable` | Visible but disabled/read-only | - -## Interceptors - -Define JavaScript interceptors for CRUD lifecycle hooks: +### Interceptors ```json { @@ -311,94 +419,150 @@ Define JavaScript interceptors for CRUD lifecycle hooks: { "commandName": "Create", "type": "Pre", - "javascript": "if(!context.commandArgs.data['Name']) { globalError = 'Name is required!'; }" + "javascript": "if (!args.getValue('Name')) { globalError = 'Name is required.'; }" } ] } ``` -See [Interceptors](interceptors.md) for details. - -## Endpoints +See [Interceptors](interceptors.md) and [Scripting API](scripting-api.md). -Define custom REST endpoints with JavaScript handlers: +### Custom Endpoints ```json { "endpoints": [ { - "name": "GetProductStats", - "route": "/api/custom/products/stats", + "name": "GetCampaignStats", + "route": "/api/custom/campaigns/stats", "method": "GET", - "requireAuthentication": false, - "javascript": "var count = await db.count('Products.Product'); return ok({ total: count });" + "requireAuthentication": true, + "requiredPermissions": ["Acme.Campaigns"], + "javascript": "var count = await db.count('Acme.Campaigns.Campaign'); return ok({ total: count });" } ] } ``` -See [Custom Endpoints](custom-endpoints.md) for details. +See [Custom Endpoints](custom-endpoints.md). + +### Event Handlers, Jobs, and Workers + +```json +{ + "eventHandlers": [ + { + "name": "NotifyCampaignCompleted", + "eventName": "Acme.Campaigns.CampaignCompleted", + "javascript": "log('Campaign completed: ' + eventData.id);" + } + ], + "backgroundJobs": [ + { + "name": "SendCampaignSummary", + "javascript": "log('Sending summary for ' + jobData.campaignId);" + } + ], + "backgroundWorkers": [ + { + "name": "CampaignCleanup", + "period": 3600000, + "javascript": "log('Cleaning campaign data.');" + } + ] +} +``` + +Background workers require either `period` in milliseconds or `cronExpression`. See [Script Actions](script-actions.md) for event handler, background job, background worker, code editor, and dry-run testing details. ## Complete Example +The complete example below shows the logical aggregate shape. Split projects store each descriptor as its own file, but field shapes are the same. + ```json { "enums": [ { - "name": "ShipmentStatus", + "name": "Acme.Campaigns.CampaignStatus", "values": [ - { "name": "Pending", "value": 0 }, - { "name": "Shipped", "value": 2 }, - { "name": "Delivered", "value": 4 } + { "name": "Draft", "value": 0 }, + { "name": "Active", "value": 1 }, + { "name": "Completed", "value": 2 } ] } ], "entities": [ { - "name": "LowCodeDemo.Products.Product", + "name": "Acme.Campaigns.Campaign", + "displayName": "Campaigns", "displayProperty": "Name", "properties": [ - { "name": "Name", "isUnique": true, "isRequired": true }, - { "name": "Price", "type": "decimal" }, - { "name": "StockCount", "type": "int" }, - { "name": "ReleaseDate", "type": "DateTime" } - ], - "ui": { "pageTitle": "Products" } - }, + { "name": "Name", "type": "string", "isRequired": true, "validators": [{ "type": "maxLength", "length": 128 }] }, + { "name": "Status", "type": "enum", "enumType": "Acme.Campaigns.CampaignStatus", "defaultValue": "0" }, + { "name": "Budget", "type": "money" }, + { "name": "StartDate", "type": "date" }, + { "name": "CoverImage", "type": "image", "fileAllowedContentTypes": ["image/*"] } + ] + } + ], + "forms": [ { - "name": "LowCodeDemo.Orders.Order", - "displayProperty": "Id", - "properties": [ - { - "name": "CustomerId", - "foreignKey": { - "entityName": "LowCodeDemo.Customers.Customer", - "access": "edit" + "name": "campaign-form", + "entityName": "Acme.Campaigns.Campaign", + "fields": [ + { "id": "name", "label": "Name", "type": "text", "binding": "Name" }, + { "id": "status", "label": "Status", "type": "select", "binding": "Status", "enumType": "Acme.Campaigns.CampaignStatus" }, + { "id": "budget", "label": "Budget", "type": "money", "binding": "Budget" }, + { "id": "startDate", "label": "Start Date", "type": "date", "binding": "StartDate" }, + { "id": "coverImage", "label": "Cover Image", "type": "image", "binding": "CoverImage" } + ], + "layout": { + "tabs": [ + { + "id": "main", + "title": "Main", + "isDefault": true, + "groups": [ + { + "id": "details", + "title": "Details", + "isDefault": true, + "fields": [ + { "fieldId": "name", "row": 0, "colSpan": 4 }, + { "fieldId": "status", "row": 1, "colSpan": 2 }, + { "fieldId": "budget", "row": 1, "colSpan": 2 }, + { "fieldId": "startDate", "row": 2, "colSpan": 2 }, + { "fieldId": "coverImage", "row": 2, "colSpan": 2 } + ] + } + ] } - }, - { "name": "TotalAmount", "type": "decimal" }, - { "name": "IsDelivered", "type": "boolean" } + ] + } + } + ], + "pageGroups": [ + { "name": "marketing", "title": "Marketing", "icon": "fa-solid fa-bullhorn", "order": 10 } + ], + "pages": [ + { + "name": "campaigns", + "title": "Campaigns", + "type": "dataGrid", + "entityName": "Acme.Campaigns.Campaign", + "group": "marketing", + "columns": [ + { "propertyName": "Name", "order": 0, "exportOrder": 0 }, + { "propertyName": "Status", "order": 1, "exportOrder": 1 }, + { "propertyName": "Budget", "order": 2, "exportOrder": 2, "exportable": false } ], - "interceptors": [ - { - "commandName": "Create", - "type": "Post", - "javascript": "context.log('Order created: ' + context.commandArgs.entityId);" - } + "filters": [ + { "propertyName": "Name", "control": "text", "defaultOperator": "contains" }, + { "propertyName": "Status", "control": "select", "defaultOperator": "equal" }, + { "propertyName": "CoverImage", "control": "exists", "defaultOperator": "hasValue" } ], - "ui": { "pageTitle": "Orders" } - }, - { - "name": "LowCodeDemo.Orders.OrderLine", - "parent": "LowCodeDemo.Orders.Order", - "properties": [ - { - "name": "ProductId", - "foreignKey": { "entityName": "LowCodeDemo.Products.Product" } - }, - { "name": "Quantity", "type": "int" }, - { "name": "Amount", "type": "decimal" } - ] + "createFormName": "campaign-form", + "editFormName": "campaign-form" } ] } @@ -406,17 +570,22 @@ See [Custom Endpoints](custom-endpoints.md) for details. ## Migration Requirements -When you modify `model.json`, you need database migrations for the changes to take effect: +Entity shape changes require database migrations before they can be used safely: -* **New entity**: `dotnet ef migrations add Added_{EntityName}` -* **New property**: `dotnet ef migrations add Added_{PropertyName}_To_{EntityName}` -* **Type change**: `dotnet ef migrations add Changed_{PropertyName}_In_{EntityName}` +* New entity +* New persisted property +* Property type change +* Required/nullability change +* Unique index change -> The same migration requirement applies when using [C# Attributes](fluent-api.md). Any change to entity structure requires an EF Core migration. +In ABP Studio, run the generated migration task for the solution. If you run the application from the command line, use the migration workflow generated by the startup template. ## See Also +* [Low-Code Designer](designer.md) +* [React Runtime](react-runtime.md) * [Attributes & Fluent API](fluent-api.md) * [Interceptors](interceptors.md) * [Custom Endpoints](custom-endpoints.md) +* [Script Actions](script-actions.md) * [Scripting API](scripting-api.md) diff --git a/docs/en/low-code/react-runtime.md b/docs/en/low-code/react-runtime.md new file mode 100644 index 0000000000..01e0e37ad9 --- /dev/null +++ b/docs/en/low-code/react-runtime.md @@ -0,0 +1,276 @@ +```json +//[doc-seo] +{ + "Description": "Configure the ABP React Low-Code runtime with configureLowCode, dynamic routes, dynamic menu items, generated pages, filters, forms, and export." +} +``` + +# React Runtime + +> **Preview:** The React low-code runtime is part of the preview Low-Code System. Runtime APIs, generated routes, metadata contracts, and UI behavior may change before general availability. + +The React runtime renders low-code pages from backend metadata. Generated low-code React applications include the required package and wiring. + +```json +{ + "dependencies": { + "@volo/abp-react-lowcode": "" + } +} +``` + +## Configure the Runtime + +Call `configureLowCode` once during React startup. Pass the application's Axios instance, notifications, localization, navigation integration, and optional extension points. + +```tsx +import { + configureLowCode, + LowCodeLocalizationProvider, +} from '@volo/abp-react-lowcode'; + +function configureLowCodeRuntime(translate?: (key: string, defaultValue: string) => string) { + configureLowCode({ + axios: api, + onError: (err) => toast.error(err.message), + onSuccess: (message) => toast.success(message), + translate, + navigate: (path) => router.navigate({ to: path }), + validators: { + customRule: (value) => value ? null : 'Value is required.', + }, + }); +} +``` + +Wrap the router with `LowCodeLocalizationProvider` when you want dynamic labels and validation messages to use the application localization pipeline. + +```tsx + + + +``` + +## Add Dynamic Routes + +Use `createDynamicRoutes` in the TanStack Router tree. + +```tsx +import { createDynamicRoutes } from '@volo/abp-react-lowcode'; + +const dynamicEntityRoute = createDynamicRoutes(rootRoute, { + beforeLoad: authGuard, +}); + +const routeTree = rootRoute.addChildren([ + indexRoute, + accountRoute, + identityRoute, + dynamicEntityRoute, +]); +``` + +Runtime pages are then available under: + +```text +/dynamic/ +/dynamic//create +/dynamic//edit/ +/dynamic// +``` + +`createDynamicRoutes` also accepts `basePath` when your application should mount dynamic pages somewhere other than `/dynamic`. + +Generated low-code React templates do not register every possible dynamic page path in TanStack Router's module augmentation. Page names come from backend metadata, so keep dynamic low-code routes out of the static route augmentation or cast the dynamic `path` inside your router navigation adapter if your application uses strict typed navigation. + +## Add Dynamic Menu Items + +Use `useMenuItems` to load menu items defined by low-code pages. Merge them with your static route configuration and apply the same permission checks used by the rest of the application. + +```tsx +import { useMenuItems } from '@volo/abp-react-lowcode'; + +const { data: dynamicMenuItems } = useMenuItems({ + enabled: isAuthenticated, +}); +``` + +Each menu item includes its page name, display name, icon, order, grouping information, and children. + +## Page Types + +The runtime includes built-in renderers for these page types: + +| Page type | Runtime behavior | +|-----------|------------------| +| `dataGrid` | Searchable, sortable CRUD grid | +| `kanban` | Card board grouped by a configured property | +| `calendar` | Calendar view using date/time properties | +| `gallery` | Card/gallery view, optionally image-backed | +| `form` | Standalone form page | +| `dashboard` | Dashboard rows with chart, list, and number visualizations | + +The generated data grid page includes: + +* Search +* Sorting +* Paging +* Action menu +* Create and edit forms +* Permission-aware commands +* Display values for lookups +* File and image fields +* Export +* Type-aware filters + +![Generated React data grid](images/runtime-data-grid.png) + +## Forms + +Create and edit forms are rendered from form metadata. Tabs, groups, labels, placeholders, controls, default values, validation rules, conditional form rules, and save actions come from the designer. + +![Generated create form](images/runtime-create-form.png) + +The runtime can render forms in a modal or on full pages. Full-page forms use the dynamic create/edit routes and the `navigate` callback configured in `configureLowCode`. + +## Filters + +Filters are rendered as an ABP-style advanced filter area. The runtime shows all configured filters and only exposes operator UI where it is useful. + +![Generated advanced filters](images/runtime-filters.png) + +File and image filters use a single `Has value` concept. The value selector controls whether the filter is applied: + +* `All` does not add a filter. +* `Yes` returns records with a value. +* `No` returns records without a value. + +![Has value options](images/runtime-filters-has-value.png) + +The URL keeps the existing `lcFilters` query parameter shape. The runtime maps user-friendly filter choices to the existing backend `FilterType` values. + +## Export + +The runtime export button opens a small menu with direct Excel and CSV actions. If the selected page has exportable file or image fields and bundle export is allowed in the Designer, the menu also shows **Files (.zip)**. Direct export uses the current search, sorting, filters, and visible exportable columns from the page definition maintained in the Low-Code Designer. Use **Export options** when users need a different row, column, or file output scope. + +Available options: + +| Option | Behavior | +|--------|----------| +| Rows: all matching records | Exports all records matching the current search, filters, and sorting | +| Rows: current page | Exports only the current page using the runtime `skipCount` and `maxResultCount` | +| Columns: visible exportable columns | Exports columns that are both visible and exportable in the current page definition, ordered by Designer export order | +| Columns: all exportable fields | Exports page fields marked exportable in the Low-Code Designer, including fields that are hidden from the grid, ordered by Designer export order | +| File/image: file name | Writes the uploaded file name, or an empty value | +| File/image: metadata columns | Writes file name, content type, size, width, and height columns | +| File/image: download links | Writes file name, temporary download URL, link expiry, content type, size, width, height, and status columns | + +Spreadsheet export stays tabular. It does not embed file bytes in cells. Use download-link columns when spreadsheet readers need a controlled way to fetch individual files, or use **Files (.zip)** when they need the actual file set. ZIP export contains `manifest.csv` and files under `files/{recordId}/{fieldName}/{safeFileName}`. The manifest reports missing, malformed, unlinked, skipped, and exported files. + +The columns available in the runtime export dialog and their default order are controlled by the page-level **Export Fields** section in the Low-Code Designer. The runtime cannot use the dialog to bypass non-exportable fields. + +The runtime first requests a short-lived token and then calls the Excel, CSV, or ZIP export endpoint. The export token is single-use and is bound to the current page, entity, tenant, child page, and foreign-access context. Temporary file links use separate short-lived tokens bound to the exported record field and blob. Text that looks like a spreadsheet formula is escaped in exported headers and cells. If a caller manually sends a non-exportable field name, the backend rejects the request. + +| Endpoint | Description | +|----------|-------------| +| `GET /api/low-code/pages/{pageName}/download-token` | Gets a short-lived token | +| `GET /api/low-code/pages/{pageName}/export/excel` | Downloads Excel | +| `GET /api/low-code/pages/{pageName}/export/csv` | Downloads CSV | +| `GET /api/low-code/pages/{pageName}/export/files` | Downloads a ZIP with selected file/image fields | +| `GET /api/low-code/pages/export/files/{token}` | Downloads one temporary file link from spreadsheet link mode | + +Child and foreign-access pages use the matching `/children/{childEntityName}` and `/foreign-access/{sourceEntityName}` page endpoints. + +Troubleshooting: + +| Symptom | Likely cause | +|---------|--------------| +| Invalid or expired download token | The token is single-use, expired, or was requested for a different page/context | +| Export row limit exceeded | Narrow the filters, export the current page, or increase `LowCode:Export:MaxRows` | +| File link expired | Re-run export; temporary file links are intentionally short-lived | +| File not exported marker | The file was missing, malformed, no longer linked to the exported record, or skipped by ZIP file count/size limits | +| ZIP bundle export disabled | Enable file bundle export for the page in the Low-Code Designer | + +## Files and Attachments + +File and image fields use the page file endpoints. Record-level attachments use attachment endpoints when attachments are enabled for the entity. + +| API | Purpose | +|-----|---------| +| `uploadPageFile` | Upload a file/image field value for a page field | +| `downloadPageFile` | Download a file/image field value | +| `buildPageFileDownloadUrl` | Build a URL for image previews or download links | +| `usePageFileObjectUrl` | Create and revoke an object URL for file previews | +| `useEntityAttachments` | Load record attachments | +| `useUploadEntityAttachments` | Upload one or more attachments | +| `useDeleteEntityAttachment` | Delete an attachment | +| `downloadEntityAttachment` | Download an attachment | + +Uploads are still validated on the backend by the configured file size and content type rules. Downloads are checked against the owning record before returning the blob, and user-provided file names should be treated as display text only. Use `usePageFileObjectUrl` for previews so object URLs are revoked when the component unmounts. + +## Hooks and Components + +The package exposes hooks for composing custom pages around the same backend API: + +| API | Purpose | +|-----|---------| +| `usePageDefinitions`, `usePageDefinition` | Load page metadata | +| `useFormDefinition` | Load a named runtime form | +| `useDashboardDefinition`, `useDashboardData` | Load dashboard metadata and data | +| `usePageData`, `usePageRecord` | Load list data and a single record | +| `usePageCreate`, `usePageUpdate`, `usePageDelete` | Mutate records | +| `usePageLookup` | Load lookup/autocomplete options | +| `usePageExport` | Export current list state | + +The package also exports renderer components such as `DynamicPage`, `DynamicEntityPage`, `DynamicKanbanRenderer`, `DynamicCalendarRenderer`, `DynamicGalleryRenderer`, `DynamicDashboardRenderer`, `DynamicFormPageRenderer`, `DynamicFilters`, `DynamicEntityForm`, and `ForeignKeyAutocomplete`. + +## Extension Points + +`configureLowCode` supports these extension points: + +| Option | Purpose | +|--------|---------| +| `validators` | Custom validation functions keyed by rule type | +| `pageRenderers` | Override built-in page renderers or add new page types | +| `fieldRenderers` | Override field rendering by field type or `customRenderer` | +| `translate` | Resolve low-code localization keys through the application | +| `navigate` | Connect full-page form navigation to the application router | + +Custom page renderers receive `PageRendererProps`. Custom field renderers receive `FieldRendererProps`. + +## Runtime API Surface + +The React runtime talks to these backend endpoints: + +| Endpoint | Purpose | +|----------|---------| +| `GET /api/low-code/ui/menu-items` | Dynamic menu tree | +| `GET /api/low-code/ui/pages` | Page list | +| `GET /api/low-code/ui/pages/{pageName}` | Page summary metadata | +| `GET /api/low-code/ui/pages/{pageName}/ui-definition` | Runtime page UI definition | +| `GET /api/low-code/ui/forms/{formName}` | Runtime form definition | +| `GET /api/low-code/ui/dashboards/{pageName}` | Dashboard definition | +| `GET /api/low-code/pages/{pageName}/data` | Page data with search, filters, sorting, and paging | +| `GET /api/low-code/pages/{pageName}/data/{id}` | Single page record | +| `POST /api/low-code/pages/{pageName}/data` | Create record | +| `PUT /api/low-code/pages/{pageName}/data/{id}` | Update record | +| `DELETE /api/low-code/pages/{pageName}/data/{id}` | Delete record | +| `GET /api/low-code/pages/{pageName}/lookup/{fieldName}` | Lookup options | +| `POST /api/low-code/pages/{pageName}/files/{fieldName}` | Upload file/image field | +| `GET /api/low-code/pages/{pageName}/data/{id}/files/{fieldName}/{blobName}` | Download file/image field | +| `GET /api/low-code/pages/{pageName}/data/{id}/attachments` | List attachments | +| `POST /api/low-code/pages/{pageName}/data/{id}/attachments` | Upload attachment | +| `DELETE /api/low-code/pages/{pageName}/data/{id}/attachments/{attachmentId}` | Delete attachment | +| `POST /api/low-code/dashboards/{pageName}/data` | Dashboard visualization data | + +## Troubleshooting + +If a generated page does not appear: + +* Confirm the page exists in the designer and has a page name. +* Confirm the user has the generated page/entity permissions. +* Confirm `createDynamicRoutes` is part of the router tree. +* Confirm `useMenuItems` is enabled for authenticated users. +* Confirm the backend host has run migrations and seed data. + +If authentication loops back to the login page, check the generated OpenIddict clients and React root URL in `appsettings.json`. diff --git a/docs/en/low-code/reference-entities.md b/docs/en/low-code/reference-entities.md index b937175c7a..3e6475f258 100644 --- a/docs/en/low-code/reference-entities.md +++ b/docs/en/low-code/reference-entities.md @@ -1,19 +1,23 @@ ```json //[doc-seo] { - "Description": "Link dynamic entities to existing C# entities like IdentityUser using Reference Entities in the ABP Low-Code System." + "Description": "Link dynamic entities to existing .NET entities like IdentityUser using Reference Entities in the ABP Low-Code System." } ``` # Reference Entities +> **Preview:** Reference entity metadata is part of the preview Low-Code System. Registration APIs, relation options, and designer behavior may change before general availability. + +Use the [Low-Code Designer](designer.md) to select reference entities after they are registered in application startup. This page explains the registration and metadata details behind that designer experience. + Reference Entities allow you to create foreign key relationships from **dynamic entities** to **existing C# entities** that live outside the Low-Code System. ## Dynamic Entities vs Reference Entities | | Dynamic Entities | Reference Entities | |---|-----------------|-------------------| -| **Definition** | Defined via `[DynamicEntity]` attribute or `model.json` | Existing C# classes (e.g., `IdentityUser`, `Tenant`) | +| **Definition** | Defined via `[DynamicEntity]` attribute or JSON descriptor files | Existing C# classes (e.g., `IdentityUser`, `Tenant`) | | **CRUD Operations** | Full CRUD (Create, Read, Update, Delete) | **Read-only** — no create/update/delete | | **UI Pages** | Auto-generated pages with data grids and forms | No UI pages | | **Permissions** | Auto-generated permissions | No permissions | @@ -22,7 +26,7 @@ Reference Entities allow you to create foreign key relationships from **dynamic ## Overview -Dynamic entities defined via [Attributes](fluent-api.md) or [model.json](model-json.md) can reference **other dynamic entities** using foreign keys. However, you may also need to link to entities that exist **outside** the Low-Code System — such as ABP's `IdentityUser`, `Tenant`, or your own C# entity classes. +Dynamic entities defined via [Attributes](fluent-api.md) or [JSON descriptor files](model-json.md) can reference **other dynamic entities** using foreign keys. However, you may also need to link to entities that exist **outside** the Low-Code System — such as ABP's `IdentityUser`, `Tenant`, or your own C# entity classes. **Reference entities** make this possible by exposing existing entities for: @@ -34,7 +38,7 @@ Dynamic entities defined via [Attributes](fluent-api.md) or [model.json](model-j ## Registering Reference Entities -Register reference entities in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer) using `AbpDynamicEntityConfig.ReferencedEntityList`: +Register reference entities in startup configuration using `AbpDynamicEntityConfig.ReferencedEntityList`: ````csharp public static async Task InitializeAsync() @@ -76,7 +80,7 @@ public void Add( > The entity type must implement `IEntity`. -## Using Reference Entities in model.json +## Using Reference Entities in JSON Descriptors Reference a registered entity in a foreign key definition: @@ -142,6 +146,6 @@ if (user) { ## See Also -* [model.json Structure](model-json.md) +* [Model Descriptor Files](model-json.md) * [Foreign Access](foreign-access.md) * [Attributes & Fluent API](fluent-api.md) diff --git a/docs/en/low-code/script-actions.md b/docs/en/low-code/script-actions.md new file mode 100644 index 0000000000..197d59ed37 --- /dev/null +++ b/docs/en/low-code/script-actions.md @@ -0,0 +1,259 @@ +```json +//[doc-seo] +{ + "Description": "Define event handlers, background jobs, background workers, script code editing, autocomplete, and dry-run testing in the ABP Low-Code Designer." +} +``` + +# Script Actions + +> **Preview:** Script actions are part of the preview Low-Code System. Descriptor fields, designer screens, and script context members may change before general availability. + +Use **Actions** in the Low-Code Designer when descriptor metadata and generated CRUD behavior are not enough. Actions can add JavaScript-backed HTTP endpoints, distributed event handlers, background jobs, and scheduled background workers. + +Custom HTTP endpoints are documented separately in [Custom Endpoints](custom-endpoints.md). This page focuses on the shared action designer experience and the event handler, background job, and background worker action types. + +## Action Types + +| Type | Use it for | Runtime trigger | +|------|------------|-----------------| +| Custom endpoint | Expose a small model-owned REST API | HTTP request to the configured route | +| Event handler | React to a named distributed event | `events.publishAsync(...)` or any compatible distributed event publisher | +| Background job | Run named JavaScript work asynchronously | `jobs.enqueueAsync(...)` | +| Background worker | Run recurring JavaScript work | `period` or `cronExpression` | + +All action scripts run server-side and use the shared [Scripting API](scripting-api.md). Available services can be enabled or disabled per action type with scripting capability profiles. + +## Script Code Editor + +The Designer uses a code editor for JavaScript fields in custom endpoints, event handlers, background jobs, background workers, and interceptors. + +The editor provides: + +* Syntax highlighting for JavaScript +* Type-aware completions for low-code services +* Entity name completions for `db`, file, image, and attachment helpers +* Entity property completions for query lambda parameters and query results +* Enum completions through `enums` and `enumValues` +* File and image field selector completions through `fileFields` and `imageFields` +* An **Available context** list for the services enabled for the selected script type + +The `fileFields` and `imageFields` globals are not lists of every entity property. They are selector trees for `File` and `Image` properties used by `files.save(...)`, `files.get(...)`, `images.save(...)`, and `images.get(...)`. + +```javascript +await files.save(fileFields.Acme.Campaigns.Campaign.Document, { + fileName: 'brief.pdf', + contentType: 'application/pdf', + base64: base64Content +}); + +await images.save(imageFields.Acme.Campaigns.Campaign.BannerImage, { + fileName: 'banner.png', + contentType: 'image/png', + base64: base64Image +}); +``` + +Regular entity fields autocomplete from entity records and query lambda parameters: + +```javascript +var campaignQuery = await db.query('Acme.Campaigns.Campaign'); +var rows = await campaignQuery + .where(campaign => campaign.Name.includes('Spring')) + .select(campaign => ({ + id: campaign.Id, + name: campaign.Name + })) + .toList(); +``` + +If the selected model layer is read-only, the Designer shows the JavaScript in a read-only editor. Switch to a writable layer before editing. + +## Event Handlers + +Event handlers run when a distributed event with the configured name is published. + +### Descriptor + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Unique event handler name | +| `eventName` | string | Distributed event name to handle | +| `javascript` | string | JavaScript handler body | +| `description` | string | Optional documentation text | + +### Context + +| Global | Description | +|--------|-------------| +| `handler` | Handler runtime metadata | +| `event` | Runtime event metadata with `name` and `data` | +| `eventName` | Event name string | +| `eventData` | Event payload | + +### Example + +```json +{ + "eventHandlers": [ + { + "name": "NotifyCampaignCompleted", + "eventName": "Acme.Campaigns.CampaignCompleted", + "description": "Logs and notifies when a campaign is completed", + "javascript": "log('Campaign completed: ' + eventData.id);\nawait email.queueAsync('ops@example.com', 'Campaign completed', eventData.id);" + } + ] +} +``` + +Publish the event from another script: + +```javascript +await events.publishAsync('Acme.Campaigns.CampaignCompleted', { + id: campaignId, + completedAt: new Date().toISOString() +}); +``` + +## Background Jobs + +Background jobs define named JavaScript handlers that can be enqueued from scripts. + +### Descriptor + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Unique job name used by `jobs.enqueueAsync(...)` | +| `javascript` | string | JavaScript job body | +| `description` | string | Optional documentation text | + +### Context + +| Global | Description | +|--------|-------------| +| `job` | Job runtime metadata | +| `jobName` | Job name string | +| `jobData` | Parsed job payload | +| `jobJsonData` | Raw JSON payload | + +### Example + +```json +{ + "backgroundJobs": [ + { + "name": "SendCampaignSummary", + "description": "Sends a summary for one campaign", + "javascript": "var campaign = await db.get('Acme.Campaigns.Campaign', jobData.campaignId);\nif (!campaign) { userFriendlyError('Campaign not found.'); }\nawait email.queueAsync(jobData.to, 'Campaign summary', campaign.Name);" + } + ] +} +``` + +Enqueue the job from another script: + +```javascript +var jobId = await jobs.enqueueAsync('SendCampaignSummary', { + campaignId: campaignId, + to: 'ops@example.com' +}, { + priority: 'Normal', + delayMs: 60000 +}); +``` + +## Background Workers + +Background workers run recurring JavaScript work on a schedule. + +### Descriptor + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Unique worker name | +| `period` | number | Period in milliseconds | +| `cronExpression` | string | Cron expression for scheduled execution | +| `javascript` | string | JavaScript worker body | +| `description` | string | Optional documentation text | + +Configure either `period` or `cronExpression`. + +### Context + +| Global | Description | +|--------|-------------| +| `worker` | Worker runtime metadata | +| `workerName` | Worker name string | + +### Example + +```json +{ + "backgroundWorkers": [ + { + "name": "CampaignCleanup", + "period": 3600000, + "description": "Runs every hour", + "javascript": "var query = await db.query('Acme.Campaigns.Campaign');\nvar stale = await query.where(c => c.Status === 0).take(100).toList();\nlog('Stale draft count: ' + stale.length);" + } + ] +} +``` + +## Test JavaScript + +Where the Designer shows **Test JavaScript**, you can run the current editor content without saving it. The built-in dry-run panel is available for custom endpoints, interceptors, event handlers, background jobs, and background workers. + +For custom endpoints, provide request data: + +* Method +* Path +* Route values +* Query values +* Headers +* Body JSON + +For interceptors, provide the command name, entity name, command data, and an optional record id. For event handlers, provide event data JSON. For background jobs and background workers, provide the job or worker input JSON that the script expects. + +When the script uses the HTTP API, you can also define outbound HTTP mocks. If a script calls `http.getAsync(...)`, `http.postAsync(...)`, or another HTTP helper, the dry-run engine returns the matching mock response instead of calling the real URL. If no mock matches, the result includes an HTTP mock miss. + +Dry-run behavior: + +| Operation | Dry-run behavior | +|-----------|------------------| +| Database writes | Executed in a transaction and rolled back | +| Low-code file/image/attachment operations | Captured as side effects without persisting files | +| Email send or queue | Captured as an `email` side effect; no email is sent | +| Event publish | Captured as an `event` side effect; no event is published | +| Background job enqueue | Captured as a `job` side effect; no job is enqueued | +| Outbound HTTP | Matched against HTTP mocks; no real HTTP call is made | +| Logs | Returned in the test result | +| Errors | Returned with type, message, and diagnostics when available | + +Dry-run results can include: + +* Endpoint response data +* Execution status and duration +* Logs +* Captured side effects +* Error details + +The endpoint dry-run still evaluates the endpoint authentication and permission metadata against the current user. If the test user is not authenticated or does not have the required permission, the dry-run returns the corresponding `401` or `403` endpoint response. + +## Operational Guidance + +* Prefer metadata and generated CRUD behavior before adding scripts. +* Keep scripts small and focused. +* Use explicit permissions for custom endpoints. +* Use `take()` and specific filters for database queries. +* Treat public unauthenticated endpoints as public API surface. +* Keep outbound HTTP, email, event, job, file, and blob limits enabled for tenant-authored scripts. +* Use capability profiles to disable services that a script type does not need. + +## See Also + +* [Low-Code Designer](designer.md) +* [Custom Endpoints](custom-endpoints.md) +* [Interceptors](interceptors.md) +* [Scripting API](scripting-api.md) +* [Model Descriptor Files](model-json.md) diff --git a/docs/en/low-code/scripting-api.md b/docs/en/low-code/scripting-api.md index a08b1107ec..dd5da7c067 100644 --- a/docs/en/low-code/scripting-api.md +++ b/docs/en/low-code/scripting-api.md @@ -7,7 +7,38 @@ # Scripting API -The Low-Code System provides a server-side JavaScript scripting engine for executing custom business logic within [interceptors](interceptors.md) and [custom endpoints](custom-endpoints.md). Scripts run in a sandboxed environment with access to a database API backed by EF Core. +> **Preview:** The Low-Code scripting API is a preview server-side JavaScript surface. Available globals, helper methods, limits, and sandbox behavior may change before general availability. + +The designer and React runtime cover the standard CRUD, form, filter, and export workflows. Use the scripting API when an interceptor, action, or custom endpoint needs server-side JavaScript. + +The Low-Code System provides a server-side JavaScript scripting engine for executing custom business logic within [interceptors](interceptors.md), [custom endpoints](custom-endpoints.md), event handlers, background jobs, and background workers. Scripts run in a sandboxed environment with access to a database API backed by EF Core. + +Scripts are wrapped in an async function, so `await` and top-level `return` are supported. + +## Designer Code Editor + +JavaScript fields in the Low-Code Designer use a code editor with syntax highlighting and low-code-aware autocomplete. The editor is available for interceptors, custom endpoints, event handlers, background jobs, and background workers. + +The **Available context** list shows the globals enabled for the current script type. The list is based on the scripting capability profile, so an application can disable services such as HTTP, email, files, or background jobs for a specific script type. + +Autocomplete covers: + +* Common globals such as `db`, `currentUser`, `emailSender`, `http`, `events`, and `jobs` +* Endpoint, event handler, background job, background worker, and interceptor context variables +* Dynamic entity names in `db.query(...)`, `db.get(...)`, file, image, and attachment helpers +* Dynamic entity properties inside query lambda parameters and query results +* Enum names and values through `enums` and `enumValues` +* File and image field selectors through `fileFields` and `imageFields` + +`fileFields` is intentionally limited to `File` properties and `imageFields` is limited to `Image` properties. They are safe selector trees for file and image helpers, not lists of every entity property. + +```javascript +await files.save(fileFields.Acme.Campaigns.Campaign.Document, { + fileName: 'brief.pdf', + contentType: 'application/pdf', + base64: base64Content +}); +``` ## Unified Database API (`db`) @@ -19,9 +50,11 @@ The `db` object is the main entry point for all data operations. * **Database-Level Execution** — all operations (filters, aggregations, joins, set operations) translate to SQL via EF Core and Dynamic LINQ. * **No in-memory processing** of large datasets. +> `db.query(entityName)` is asynchronous. Always `await` it before chaining query methods, and `await` the terminal operation such as `toList()`, `count()`, `first()`, or `sum()`. + ```javascript // Immutable pattern — each call creates a new builder -var baseQuery = db.query('Entity').where(x => x.Active); +var baseQuery = (await db.query('Entity')).where(x => x.Active); var cheap = baseQuery.where(x => x.Price < 100); // baseQuery unchanged var expensive = baseQuery.where(x => x.Price > 500); // baseQuery unchanged ``` @@ -31,13 +64,15 @@ var expensive = baseQuery.where(x => x.Price > 500); // baseQuery unchanged ### Basic Queries ```javascript -var products = await db.query('LowCodeDemo.Products.Product') +var productQuery = await db.query('LowCodeDemo.Products.Product'); +var products = await productQuery .where(x => x.Price > 100) .orderBy(x => x.Price) .take(10) .toList(); -var result = await db.query('LowCodeDemo.Products.Product') +var filteredProductQuery = await db.query('LowCodeDemo.Products.Product'); +var result = await filteredProductQuery .where(x => x.Price > 100 && x.Price < 500) .where(x => x.StockCount > 0) .orderByDescending(x => x.Price) @@ -92,16 +127,19 @@ var minPrice = 100; var config = { minStock: 10 }; var nested = { range: { min: 50, max: 200 } }; -var result = await db.query('Entity').where(x => x.Price > minPrice).toList(); -var result2 = await db.query('Entity').where(x => x.StockCount > config.minStock).toList(); -var result3 = await db.query('Entity').where(x => x.Price >= nested.range.min).toList(); +var query = await db.query('Entity'); + +var result = await query.where(x => x.Price > minPrice).toList(); +var result2 = await query.where(x => x.StockCount > config.minStock).toList(); +var result3 = await query.where(x => x.Price >= nested.range.min).toList(); ``` ### Contains / IN Operator ```javascript var targetPrices = [50, 100, 200]; -var products = await db.query('Entity') +var query = await db.query('Entity'); +var products = await query .where(x => targetPrices.includes(x.Price)) .toList(); ``` @@ -109,7 +147,8 @@ var products = await db.query('Entity') ### Select Projection ```javascript -var projected = await db.query('LowCodeDemo.Products.Product') +var productQuery = await db.query('LowCodeDemo.Products.Product'); +var projected = await productQuery .where(x => x.Price > 0) .select(x => ({ ProductName: x.Name, ProductPrice: x.Price })) .toList(); @@ -120,7 +159,8 @@ var projected = await db.query('LowCodeDemo.Products.Product') ### Explicit Joins ```javascript -var orderLines = await db.query('LowCodeDemo.Orders.OrderLine') +var orderLineQuery = await db.query('LowCodeDemo.Orders.OrderLine'); +var orderLines = await orderLineQuery .join('LowCodeDemo.Products.Product', 'p', (ol, p) => ol.ProductId === p.Id) .take(10) .toList(); @@ -135,7 +175,8 @@ orderLines.forEach(line => { ### Left Join ```javascript -var orders = await db.query('LowCodeDemo.Orders.Order') +var orderQuery = await db.query('LowCodeDemo.Orders.Order'); +var orders = await orderQuery .leftJoin('LowCodeDemo.Products.Product', 'p', (o, p) => o.CustomerId === p.Id) .toList(); @@ -149,7 +190,8 @@ orders.forEach(order => { ### LINQ-Style Join ```javascript -db.query('Order') +var orderQuery = await db.query('Order'); +orderQuery .join('LowCodeDemo.Products.Product', o => o.ProductId, p => p.Id) @@ -158,9 +200,11 @@ db.query('Order') ### Join with Filtered Query ```javascript -var expensiveProducts = db.query('Product').where(p => p.Price > 100); +var productQuery = await db.query('Product'); +var expensiveProducts = productQuery.where(p => p.Price > 100); -var orders = await db.query('OrderLine') +var orderLineQuery = await db.query('OrderLine'); +var orders = await orderLineQuery .join(expensiveProducts, ol => ol.ProductId, p => p.Id) @@ -179,8 +223,9 @@ Set operations execute at the database level using SQL: | `except(query)` | `EXCEPT` | Elements in first, not second | ```javascript -var cheap = db.query('Product').where(x => x.Price <= 100); -var popular = db.query('Product').where(x => x.Rating > 4); +var productQuery = await db.query('Product'); +var cheap = productQuery.where(x => x.Price <= 100); +var popular = productQuery.where(x => x.Rating > 4); var bestDeals = await cheap.intersect(popular).toList(); var underrated = await cheap.except(popular).toList(); @@ -197,18 +242,21 @@ All aggregations execute as SQL statements: | `min(x => x.Property)` | `SELECT MIN(...)` | `Promise` | | `max(x => x.Property)` | `SELECT MAX(...)` | `Promise` | | `distinct(x => x.Property)` | `SELECT DISTINCT ...` | `Promise` | -| `groupBy(x => x.Property)` | `GROUP BY ...` | `Promise` | +| `groupBy(x => x.Property)` | `GROUP BY ...` | `QueryBuilder` | ```javascript -var totalValue = await db.query('Product').sum(x => x.Price); -var avgPrice = await db.query('Product').where(x => x.InStock).average(x => x.Price); -var cheapest = await db.query('Product').min(x => x.Price); +var productQuery = await db.query('Product'); + +var totalValue = await productQuery.sum(x => x.Price); +var avgPrice = await productQuery.where(x => x.InStock).average(x => x.Price); +var cheapest = await productQuery.min(x => x.Price); ``` ### GroupBy with Select ```javascript -var grouped = await db.query('Product') +var productQuery = await db.query('Product'); +var grouped = await productQuery .groupBy(x => x.Category) .select(g => ({ Category: g.Key, @@ -237,7 +285,8 @@ var grouped = await db.query('Product') ### GroupBy with Items ```javascript -var grouped = await db.query('Product') +var productQuery = await db.query('Product'); +var grouped = await productQuery .groupBy(x => x.Category) .select(g => ({ Category: g.Key, @@ -251,18 +300,19 @@ var grouped = await db.query('Product') | Limit | Default | Description | |-------|---------|-------------| -| `MaxGroupCount` | No limit | Maximum groups | +| `MaxGroupCount` | 500 | Maximum groups; set to `null` to disable this limit | ## Math Functions Math functions translate to SQL functions (ROUND, FLOOR, CEILING, ABS, etc.): ```javascript -var products = await db.query('Product') +var productQuery = await db.query('Product'); +var products = await productQuery .where(x => Math.round(x.Price) > 100) .toList(); -var result = await db.query('Product') +var result = await productQuery .where(x => Math.abs(x.Balance) < 10 && Math.floor(x.Rating) >= 4) .toList(); ``` @@ -274,7 +324,9 @@ Direct CRUD methods on the `db` object: | Method | Description | Returns | |--------|-------------|---------| | `db.get(entityName, id)` | Get by ID | `Promise` | +| `db.getList(entityName, take?)` | Get a list with an optional limit | `Promise` | | `db.getCount(entityName)` | Get count | `Promise` | +| `db.count(entityName)` | Alias for `db.getCount(entityName)` | `Promise` | | `db.exists(entityName)` | Check if any records exist | `Promise` | | `db.insert(entityName, entity)` | Insert new | `Promise` | | `db.update(entityName, entity)` | Update existing | `Promise` | @@ -304,47 +356,300 @@ var updated = await db.update('LowCodeDemo.Products.Product', { await db.delete('LowCodeDemo.Products.Product', id); ``` -## Context Object +## Script Context + +Scripts receive a `context` object and common global shortcuts. Available services can be enabled or disabled per script type with capability profiles. + +### Common Services + +| Global | Context property | Description | +|--------|------------------|-------------| +| `db` | `context.db` | Query and CRUD API | +| `user`, `currentUser` | `context.currentUser` | Current user information and claims | +| `tenant`, `currentTenant` | `context.currentTenant` | Current tenant information | +| `email`, `emailSender` | `context.emailSender` | Email send and queue helpers | +| `config` | `context.config` | Filtered application configuration reader | +| `http` | `context.http` | Hardened outbound HTTP client | +| `auth`, `authorization` | `context.authorization` | Permission checks | +| `settings` | `context.settings` | Filtered setting provider | +| `features` | `context.features` | Feature checks | +| `events` | `context.events` | Distributed event publishing | +| `jobs` | `context.jobs` | Dynamic background job enqueueing | +| `encryption` | `context.encryption` | String encryption and decryption | +| `textTemplating` | `context.textTemplating` | ABP text template rendering | +| `blob` | `context.blob` | Base64 blob storage wrapper | +| `files` | `context.files` | Low-code file field helper | +| `images` | `context.images` | Low-code image field helper | +| `attachments` | `context.attachments` | Record attachment helper | +| `fileFields` | `context.fileFields` | File field selector tree | +| `imageFields` | `context.imageFields` | Image field selector tree | +| `enums`, `enumValues` | enum registry | Low-code enum value registry | +| `log`, `logWarning`, `logError` | logging methods | Script logging | + +Global helpers are also available: + +| Helper | Description | +|--------|-------------| +| `guid()` | Generates a GUID string | +| `userFriendlyError(message)` | Throws a `UserFriendlyException` | +| `businessError(message, code?)` | Throws a `BusinessException` | + +### Interceptor Context + +Interceptors add `args` and `commandArgs`: + +| Property / Method | Description | +|-------------------|-------------| +| `commandArgs.data` | Entity data dictionary for create/update | +| `commandArgs.entityId` | Entity ID for update/delete | +| `commandArgs.commandName` | `Create`, `Update`, or `Delete` | +| `commandArgs.entityName` | Full entity name | +| `commandArgs.getValue(name)` | Get a property value | +| `commandArgs.setValue(name, value)` | Set a property value | +| `commandArgs.hasValue(name)` | Check whether the input contains a property | +| `commandArgs.removeValue(name)` | Remove a property from the input | + +Set `globalError` to abort an operation with a user-facing error: + +```javascript +if (!args.getValue('Name')) { + globalError = 'Name is required.'; +} +``` + +### Custom Endpoint Context + +Custom endpoints add request globals and response helpers. See [Custom Endpoints](custom-endpoints.md) for details. + +| Variable | Description | +|----------|-------------| +| `request` | Full request object | +| `route`, `params` | Route values | +| `query` | Query string values | +| `body` | Request body | +| `headers` | Selected safe request headers | + +### Event, Job, and Worker Context + +| Script type | Additional globals | +|-------------|--------------------| +| Event handler | `handler`, `event`, `eventName`, `eventData` | +| Background job | `job`, `jobName`, `jobData`, `jobJsonData` | +| Background worker | `worker`, `workerName` | + +Event handlers, background jobs, and background workers are configured in the Designer **Actions** section or in JSON descriptor files. See [Script Actions](script-actions.md) for descriptors, examples, and dry-run testing. + +Event handler example: + +```javascript +log('Received event ' + eventName); + +if (eventData && eventData.campaignId) { + await jobs.enqueueAsync('SendCampaignSummary', { + campaignId: eventData.campaignId + }); +} +``` + +Background job example: + +```javascript +var campaign = await db.get('Acme.Campaigns.Campaign', jobData.campaignId); +if (!campaign) { + userFriendlyError('Campaign not found.'); +} + +await email.queueAsync(jobData.to, 'Campaign summary', campaign.Name); +``` + +Background worker example: + +```javascript +var campaignQuery = await db.query('Acme.Campaigns.Campaign'); +var staleCount = await campaignQuery + .where(campaign => campaign.Status === 0) + .count(); + +log('Stale draft campaigns: ' + staleCount); +``` + +## Service Helpers + +### HTTP + +The `http` helper supports outbound requests with timeout, response-size, host, and HTTPS policy checks. + +| Method | Description | +|--------|-------------| +| `http.getAsync(url, options?)` | GET | +| `http.postAsync(url, body?, options?)` | POST | +| `http.putAsync(url, body?, options?)` | PUT | +| `http.patchAsync(url, body?, options?)` | PATCH | +| `http.deleteAsync(url, options?)` | DELETE | +| `http.requestAsync(method, url, bodyOrOptions?, options?)` | Custom method | + +Options include `headers`, `query`, `timeoutMs`, `contentType`, and `responseType` (`json`, `text`, or `base64`). + +### Authorization, Settings, Features, and Config + +```javascript +if (await auth.isGrantedAsync('Acme.Campaigns.Create')) { + var enabled = await features.isEnabledAsync('Acme.Campaigns'); + var threshold = await settings.getIntAsync('Acme.Campaigns.Threshold', 10); + var baseUrl = config.get('ExternalApi:BaseUrl'); +} +``` + +### Events and Background Jobs + +```javascript +await events.publishAsync('Acme.Campaigns.CampaignCompleted', { id: campaignId }); + +await jobs.enqueueAsync('SendCampaignSummary', { campaignId: campaignId }, { + priority: 'Normal', + delayMs: 60000 +}); +``` + +### Files, Images, and Attachments + +The file helpers use low-code page services so permissions, file validation, linked-blob checks, and foreign access stay consistent with the runtime. + +| Helper | Purpose | +|--------|---------| +| `files.parse(value)` | Parse a stored file value | +| `files.format(value, includeSize?)` | Format a file display value | +| `files.save(...)` / `images.save(...)` | Save file or image content | +| `files.get(...)` / `images.get(...)` | Read file or image content | +| `files.upload(entityName, fieldName, fileInput, options?)` | Upload field content | +| `attachments.list(...)` | List record attachments | +| `attachments.upload(...)` / `attachments.save(...)` | Upload a record attachment | +| `attachments.get(...)` / `attachments.download(...)` | Download a record attachment | +| `attachments.delete(...)` | Delete a record attachment | + +File content is passed as base64 data. File operations are subject to configured read/write size limits. + +Use `fileFields` and `imageFields` when you want typed selectors for file or image properties: + +```javascript +await files.save(fileFields.Acme.Campaigns.Campaign.Document, { + fileName: 'brief.pdf', + contentType: 'application/pdf', + base64: base64Content +}); + +var content = await images.get( + imageFields.Acme.Campaigns.Campaign.BannerImage, + campaignId +); +``` + +The selector path is based on the full entity name and the `File` or `Image` property name. Record-level attachments are entity-level, not property-level, so they use the `attachments` helper instead of field selectors. + +### Email -Available in [interceptors](interceptors.md): +The `email` and `emailSender` globals use the configured ABP `IEmailSender`. -| Property | Type | Description | -|----------|------|-------------| -| `context.commandArgs` | object | Command arguments (data, entityId, commandName, entityName) | -| `context.commandArgs.getValue(name)` | function | Get property value | -| `context.commandArgs.setValue(name, value)` | function | Set property value | -| `context.commandArgs.hasValue(name)` | function | Check if a property exists | -| `context.commandArgs.removeValue(name)` | function | Remove a property value | -| `context.currentUser` | object | Current user info (see [Interceptors](interceptors.md) for full list) | -| `context.emailSender` | object | Email sending (`sendAsync`, `sendHtmlAsync`) | -| `context.log(msg)` | function | Log an informational message | -| `context.logWarning(msg)` | function | Log a warning message | -| `context.logError(msg)` | function | Log an error message | +| Method | Description | +|--------|-------------| +| `email.sendAsync(to, subject, body)` | Send plain text email | +| `email.sendAsync(from, to, subject, body)` | Send plain text email with explicit sender | +| `email.sendHtmlAsync(to, subject, htmlBody)` | Send HTML email | +| `email.sendHtmlAsync(from, to, subject, htmlBody)` | Send HTML email with explicit sender | +| `email.queueAsync(to, subject, body)` | Queue plain text email | +| `email.queueAsync(from, to, subject, body)` | Queue plain text email with explicit sender | +| `email.queueHtmlAsync(to, subject, htmlBody)` | Queue HTML email | +| `email.queueHtmlAsync(from, to, subject, htmlBody)` | Queue HTML email with explicit sender | + +Email operations validate the recipient address, apply allowed or blocked domain rules when configured, and enforce the per-execution email limit. + +```javascript +if (email.isAvailable) { + await email.queueAsync( + 'ops@example.com', + 'Campaign completed', + 'Campaign ' + campaignId + ' completed.' + ); +} +``` + +### Test JavaScript Dry Run + +The Designer can run JavaScript without saving it where the **Test JavaScript** panel is available. The built-in dry-run panel supports custom endpoints, interceptors, event handlers, background jobs, and background workers. + +Dry-run execution returns the endpoint response or script status, logs, captured side effects, duration, and error diagnostics. + +| Operation | Dry-run behavior | +|-----------|------------------| +| Database writes | Executed in a transaction and rolled back | +| File, image, and attachment operations | Captured as side effects without persisting files | +| Email send or queue | Captured as an `email` side effect; no email is sent | +| Event publish | Captured as an `event` side effect; no event is published | +| Background job enqueue | Captured as a `job` side effect; no job is enqueued | +| Outbound HTTP | Resolved from configured HTTP mocks; no real HTTP request is sent | +| Logs | Returned in the result | +| Errors | Returned with type, message, and diagnostics when available | + +For endpoint dry runs, the request method, path, route values, query values, headers, and body are supplied by the test panel. Endpoint authentication and permission metadata are checked against the current user. For interceptor dry runs, the test panel supplies command metadata and command data. For event handler dry runs, it supplies `eventData`. For background job and worker dry runs, it supplies the job or worker input JSON. ## Configuration -You can configure scripting limits using `AbpLowCodeScriptingOptions` in your module's `ConfigureServices` method: +Configure scripting limits with the `LowCode:Scripting` configuration section or `AbpLowCodeScriptingOptions`. ```csharp Configure(options => { - // Script execution limits (null = no limit) options.Script.Timeout = TimeSpan.FromMinutes(1); options.Script.MaxStatements = 100_000; - options.Script.MaxMemoryBytes = 512 * 1024 * 1024; // 512 MB - options.Script.MaxRecursionDepth = 500; + options.Script.MaxMemoryBytes = 128 * 1024 * 1024; + options.Script.MaxRecursionDepth = 64; - // Query API limits (null = no limit) options.Query.MaxLimit = 10_000; options.Query.DefaultLimit = 1000; options.Query.MaxExpressionNodes = 200; - options.Query.MaxExpressionDepth = 20; + options.Query.MaxExpressionDepth = 10; options.Query.MaxArraySize = 500; options.Query.MaxGroupCount = 500; + + options.Capabilities.Endpoint.EnableHttp = false; }); ``` -All limits default to `null` (no limit). Configure them based on your security requirements and expected workload. +Most numeric limits can be set to `null` to explicitly disable that limit. Keep the defaults for untrusted or tenant-authored scripts. + +### Capability Profiles + +The same services are not required in every script type. Capability profiles let you disable services per execution type: + +```json +{ + "LowCode": { + "Scripting": { + "Capabilities": { + "Interception": { + "EnableDb": true, + "EnableHttp": false + }, + "Endpoint": { + "EnableDb": true, + "EnableHttp": true + }, + "EventHandler": { + "EnableDb": true + }, + "BackgroundJob": { + "EnableDb": true + }, + "BackgroundWorker": { + "EnableDb": true + } + } + } + } +} +``` + +Each profile supports flags such as `EnableDb`, `EnableCurrentUser`, `EnableCurrentTenant`, `EnableEmail`, `EnableConfig`, `EnableHttp`, `EnableAuthorization`, `EnableSettings`, `EnableFeatures`, `EnableEvents`, `EnableBackgroundJobs`, `EnableEncryption`, `EnableTextTemplating`, `EnableBlob`, and `EnableFiles`. ## Security @@ -352,22 +657,38 @@ All limits default to `null` (no limit). Configure them based on your security r | Constraint | Default | Configurable | |------------|---------|--------------| -| Script Timeout | No limit | Yes | -| Max Statements | No limit | Yes | -| Memory Limit | No limit | Yes | -| Recursion Depth | No limit | Yes | +| Script Timeout | 30 seconds | Yes | +| Max Statements | 100,000 | Yes | +| Memory Limit | 128 MB | Yes | +| Recursion Depth | 64 | Yes | +| Max Script Length | 500,000 characters | Yes | | CLR Access | Disabled | No | ### Query Security Limits | Limit | Default | Description | |-------|---------|-------------| -| MaxExpressionNodes | No limit | Max AST nodes per expression | -| MaxExpressionDepth | No limit | Max nesting depth | -| MaxLimit (take) | No limit | Max records per query | -| DefaultLimit | No limit | Default if `take()` not specified | -| MaxArraySize (includes) | No limit | Max array size for IN operations | -| MaxGroupCount | No limit | Max groups in GroupBy | +| MaxExpressionNodes | 200 | Max AST nodes per expression | +| MaxExpressionDepth | 10 | Max nesting depth | +| MaxLimit (take) | 10,000 | Max records per query | +| DefaultLimit | 1,000 | Default if `take()` is not specified | +| MaxArraySize (includes) | 500 | Max array size for IN operations | +| MaxGroupCount | 500 | Max groups in GroupBy | + +### Integration Limits + +| Area | Default | +|------|---------| +| HTTP timeout | 30 seconds | +| HTTP response size | 5 MB | +| HTTP requests per execution | 50 | +| HTTP blocked hosts | `localhost` and private IP ranges | +| Email sends per execution | 5 | +| Blob read/write size | 10 MB read, 5 MB write | +| Low-code file read/write size | 10 MB read, 5 MB write | +| Event publishes per execution | 10 | +| Background jobs per execution | 10 | +| Endpoint response body | 1 MB | ### Property Whitelist @@ -380,7 +701,8 @@ All values are parameterized: ```javascript var malicious = "'; DROP TABLE Products;--"; // Safely treated as a literal string — no injection -var result = await db.query('Entity').where(x => x.Name.includes(malicious)).count(); +var query = await db.query('Entity'); +var result = await query.where(x => x.Name.includes(malicious)).count(); ``` ### Blocked Features @@ -395,9 +717,16 @@ if (!context.commandArgs.getValue('Email').includes('@')) { throw new Error('Valid email is required'); } +// User-friendly ABP exception +userFriendlyError('The campaign is not ready to publish.'); + +// Business exception with a code +businessError('Budget is exceeded.', 'Acme.Campaigns:BudgetExceeded'); + // Try-catch for safe execution try { - var products = await db.query('Entity').where(x => x.Price > 0).toList(); + var query = await db.query('Entity'); + var products = await query.where(x => x.Price > 0).toList(); } catch (error) { context.log('Query failed: ' + error.message); } @@ -422,7 +751,8 @@ try { var productId = context.commandArgs.getValue('ProductId'); var quantity = context.commandArgs.getValue('Quantity'); -var product = await db.query('LowCodeDemo.Products.Product') +var productQuery = await db.query('LowCodeDemo.Products.Product'); +var product = await productQuery .where(x => x.Id === productId) .first(); @@ -435,10 +765,12 @@ context.commandArgs.setValue('TotalAmount', product.Price * quantity); ### Sales Dashboard (Custom Endpoint) ```javascript -var totalOrders = await db.query('LowCodeDemo.Orders.Order').count(); -var delivered = await db.query('LowCodeDemo.Orders.Order') +var orderQuery = await db.query('LowCodeDemo.Orders.Order'); + +var totalOrders = await orderQuery.count(); +var delivered = await orderQuery .where(x => x.IsDelivered === true).count(); -var revenue = await db.query('LowCodeDemo.Orders.Order') +var revenue = await orderQuery .where(x => x.IsDelivered === true).sum(x => x.TotalAmount); return ok({ @@ -452,4 +784,5 @@ return ok({ * [Interceptors](interceptors.md) * [Custom Endpoints](custom-endpoints.md) -* [model.json Structure](model-json.md) +* [Script Actions](script-actions.md) +* [Model Descriptor Files](model-json.md) diff --git a/docs/en/others/penetration-test-report.md b/docs/en/others/penetration-test-report.md index cc80d39b8e..8ca58a57c9 100644 --- a/docs/en/others/penetration-test-report.md +++ b/docs/en/others/penetration-test-report.md @@ -7,51 +7,25 @@ # ABP Penetration Test Report -The ABP Commercial MVC `v10.0.1` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. +The ABP Commercial MVC `v10.4.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was scanned on a local HTTPS address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive. -In the next sections, you will find the affected URLs, attack parameters (request-body), alert descriptions, false-positive explanations, and fixes for the issues. Some positive alerts are already fixed or needed additional actions that can be taken by you. The issue links for the fixes are mentioned in each positive alert. +In the next sections, you will find the affected URLs, attack parameters (request-body), alert descriptions, false-positive explanations, and fixes for the issues. Some alerts need additional actions that can be taken by you. ## Alerts There are high _(red flag)_, medium _(orange flag)_, low _(yellow flag)_, and informational _(blue flag)_ alerts. -![penetration-test-10.0.1](../images/pen-test-alert-list-10.1.png) +![penetration-test-10.4.0](../images/pen-test-alert-list-10.4.png) > The informational alerts are not mentioned in this document. These alerts don't raise any risks for your application and they are optional. -### Cross Site Scripting (Reflected) [Risk: High] - Positive - -- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddMemberModal?title=SelectAUser&organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* -- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddRoleModal?organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* -- *[GET] - https://localhost:44349/Saas/Host/Tenants/ImpersonateTenantModal?tenantId=...&tenantName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* - -**Description**: - -Cross-site Scripting (XSS) is an attack technique that involves echoing attacker-supplied code into a user's browser instance. - -**Explanation**: - -This is a **Positive** alert. The application reflects the `OrganizationUnitName` and `tenantName` parameters without proper encoding in the modal headers, allowing for the execution of arbitrary JavaScript. We have created an **internal issue** to track this vulnerability, and it will be fixed in the next release. - -### PII Disclosure [Risk: High] - False Positive - -- *[GET] - https://localhost:44349/* (Evidence: 639002492030480000) -- *[GET] - https://localhost:44349/?page=...* - -**Description**: - -The response contains Personally Identifiable Information, such as CC number, SSN and similar sensitive data. - -**Explanation**: - -This is a **false-positive** alert. The detected numbers (e.g., `639002492030480000`) are cache-busting timestamps (`_v` parameter) generated by the framework for static assets. They coincidentally match the pattern of Credit Card numbers (pattern matching) but are not sensitive data. - ### Path Traversal [Risk: High] - False Positive -- *[GET] - https://localhost:44349/Account/Login?returnUrl=Login* -- *[GET] - https://localhost:44349/api/account/security-logs?action=\security-logs* +- *[GET] - https://localhost:44348/api/audit-logging/audit-logs?httpMethod=audit-logs&sorting=executionTime+desc&skipCount=0&maxResultCount=10* +- *[GET] - https://localhost:44348/Account/ForgotPassword?returnUrl=%5CForgotPassword* +- *[GET] - https://localhost:44348/Account/Login?ReturnUrl=%2FAccount%2FManage* **Description**: @@ -63,8 +37,9 @@ This is a **false-positive** alert. ABP Framework automatically validates `retur ### SQL Injection [Risk: High] - False Positive -- *[GET] - https://localhost:44349/AbpPermissionManagement/PermissionManagementModal?providerKey=AbpSolution16711_Swagger+AND+1%3D1+--+* -- *[GET] - https://localhost:44349/Account/Manage?CurrentPassword=ZAP%27+AND+%271%27%3D%271%27+--+* +- *[GET] - https://localhost:44348/api/audit-logging/audit-logs/entity-changes?entityTypeFullName=%27+AND+%271%27%3D%271%27+--+&sorting=changeTime+desc&skipCount=0&maxResultCount=10* +- *[GET] - https://localhost:44348/api/identity/claim-types?filter=&skipCount=0%27+AND+%271%27%3D%271%27+--+&maxResultCount=10* +- *[GET] - https://localhost:44348/api/language-management/language-texts?sorting=name+asc+AND+1%3D1+--+&skipCount=0&maxResultCount=10* **Description**: @@ -74,37 +49,14 @@ SQL injection may be possible. This is a **false-positive** alert. ABP Framework uses Entity Framework Core, which inherently uses parameterized queries, preventing standard SQL injection attacks. Manual verification showed that injecting SQL syntax into parameters like `providerKey` results in the input being treated as a literal string (resulting in no match or default behavior) rather than altering the query structure. -### SQL Injection - SQLite [Risk: High] - False Positive - -- *[POST] - https://localhost:44349/Account/ForgotPassword?returnUrl=%2FAccount%2FManage* (Attack: `case randomblob(100000) ...`) -- *[POST] - https://localhost:44349/FeatureManagement/FeatureManagementModal* - -**Description**: - -SQL injection may be possible. - -**Explanation**: +### Content Security Policy (CSP) Header Not Set [Risk: Medium] — Application Specific Configuration -This is a **false-positive** alert. Similar to the standard SQL Injection alert, the application uses parameterized queries. The detected delays are likely due to application processing variations or network latency rather than successful SQL injection. - -### Content Security Policy (CSP) Header Not Set [Risk: Medium] — Positive (Fixed) - -- *[GET] — https://localhost:44349* -- *[GET] — https://localhost:44349/AuditLogs* -- *[GET] — https://localhost:44349/CookiePolicy* -- *[GET] — https://localhost:44349/Gdpr/PersonalData* -- *[GET] — https://localhost:44349/Identity/ClaimTypes/{0}* (create & edit modal URLs - also there are other modal related URLs...) -- *[GET] — https://localhost:44349/AbpPermissionManagement/PermissionManagementModal?providerName=R&providerKey=role&providerKeyDisplayName=role* -- *[GET] — https://localhost:44349/Abp/MultiTenancy/TenantSwitchModal* -- *[GET] — https://localhost:44349/Account/AuthorityDelegation/AuthorityDelegationModal* -- *[GET] — https://localhost:44349/Account/AuthorityDelegation/DelegateNewUserModal* -- *[GET] — https://localhost:44349/Account/ForgotPassword _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/ExternalLogins _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/SecurityLogs _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/Login _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/Register _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/Manage _(other several account URLS)_* -- *[GET] — https://localhost:44349/Account/Sessions _(other several account URLS)_* +- *[GET] — https://localhost:44348/* +- *[GET] — https://localhost:44348/?page=%2FAccount%2F~%2FAccount%2FLogin* +- *[GET] — https://localhost:44348/Abp/MultiTenancy/TenantSwitchModal* +- *[GET] — https://localhost:44348/Account/ForgotPassword* +- *[GET] — https://localhost:44348/Account/Login _(other several account URLs)_* +- *[GET] — https://localhost:44348/Account/Register _(other several account URLs)_* **Description:** @@ -114,7 +66,7 @@ Content Security Policy (CSP) is an added layer of security that helps to detect Ensure that your web server, application server, load balancer, etc. are configured to set the `Content-Security-Policy` header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+, and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+. -This vulnerability has fixed in ABP v7.0. You can check the related issue from [github.com/abpframework/abp/issues/14173](https://github.com/abpframework/abp/issues/14173). You can configure the `AbpSecurityHeadersOptions` and set the `UseContentSecurityPolicyHeader` property as *true* to add the `Content-Security-Policy` header into your application: +ABP provides CSP support through `AbpSecurityHeadersOptions`, but `UseContentSecurityPolicyHeader` is `false` by default because each application may need a different CSP depending on scripts, styles, external identity providers, CDNs, and integrations. Configure `AbpSecurityHeadersOptions` and set the `UseContentSecurityPolicyHeader` property as *true* to add the `Content-Security-Policy` header into your application: ```csharp Configure(options => @@ -127,12 +79,9 @@ Configure(options => ### Format String Error [Risk: Medium] - False Positive -- *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A&returnUrl=%2F&uiCulture=ar* (with combination of different parameters) -- *[GET] — https://localhost:44349/Abp/ApplicationLocalizationScript?cultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A* (with combination of different parameters) -- *[GET] — https://localhost:44349/api/language-management/language-texts?filter=aa&resourceName=&baseCultureName=es&targetCultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A&getOnlyEmptyValues=false&sorting=name+asc&skipCount=0&maxResultCount=10* (with combination of different parameters) -- *[GET] — https://localhost:44349/LanguageManagement/Texts/Edit?name=IncorrectCaptchaAnswer&targetCultureName=sv&resourceName=AbpAccount&baseCultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A* (with combination of different parameters) -- *[POST] — https://localhost:44349/Account/Login?ReturnUrl=%2FSettingManagement* -- *[POST] — https://localhost:44349/Account/Manage* (with combination of different parameters) +- *[GET] — https://localhost:44348/Abp/ApplicationLocalizationScript?cultureName=ZAP%25n%25s%25n%25s%0A* (with combination of different parameters) +- *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=ZAP%25n%25s%25n%25s%0A&returnUrl=%2F&uiCulture=en-GB* (with combination of different parameters) +- *[GET] — https://localhost:44348/Account/Login* (with combination of different parameters) **Description:** @@ -152,9 +101,9 @@ The second URL is also a **false-positive** alert because there is no bad charac ### XSLT Injection [Risk: Medium] - False Positive -- *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E&returnUrl=%2F&uiCulture=tr _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/Account/ForgotPassword _(same URL with different parameters...)_* -- *[GET] — https://localhost:44349/SaasWidgets/LatestTenants _(same URL with different parameters...)_* +- *[GET] — https://localhost:44348/api/openiddict/applications?id=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E _(same payload with different parameters...)_* +- *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E&returnUrl=%2F&uiCulture=en-GB _(same payload with different parameters...)_* +- *[GET] — https://localhost:44348/?page=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E _(same payload with different parameters...)_* **Description**: @@ -162,16 +111,11 @@ Injection using XSL transformations may be possible and may allow an attacker to **Explanation**: -This is a **false-positive** alert. v9.0 uses .NET 9 and the XSLT transformation is not possible on .NET5 or higher. +This is a **false-positive** alert. ABP v10.4.x uses .NET 10, and the scanned endpoints do not execute user-supplied XSLT. The local validation did not expose XSLT execution or system property output. ### Application Error Disclosure [Risk: Low] — False Positive -- *[POST] — https://localhost:44349/Account/ImpersonateUser* -- *[GET] — https://localhost:44349/Account/ExternalLogins* -- *[GET] — https://localhost:44349/OrganizationUnits* -- *[GET] — https://localhost:44349/HostDashboard* -- *[GET] — https://localhost:44349/Saas/Host/Editions* -- *[GET] — https://localhost:44349/Saas/Host/Tenants* +- *[GET] — https://localhost:44348/Account/ExternalLogins* **Description:** @@ -179,35 +123,34 @@ The reported pages contain an error/warning message that may disclose sensitive **Explanation:** -This vulnerability was reported as a **positive** alert because the application ran in `Development` mode. ABP throws exceptions for developers in the `Development` environment. We set the environment to `Production` and re-run the test, then the server sent a *500-Internal Error* without the error disclosed. Therefore this alert is **false-positive**. Further information can be found in the following issue: [github.com/abpframework/abp/issues/14177](https://github.com/abpframework/abp/issues/14177#issuecomment-1268206947). +This vulnerability was reported as a **positive** alert because the application ran in `Development` mode. ABP throws exceptions for developers in the `Development` environment. Production mode returned a generic error page in the local validation, without framework stack traces or database details. Therefore this alert is **false-positive** for production deployments. Further information can be found in the following issue: [github.com/abpframework/abp/issues/14177](https://github.com/abpframework/abp/issues/14177#issuecomment-1268206947). ### Cookie No `HttpOnly` Flag [Risk: Low] — Positive (No need for a fix) -* *[GET] — https://localhost:44349 (and other several URLs...)* -* *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ar&returnUrl=%2FAccount%2FForgotPassword%3FreturnUrl%3D%2522%252F%253E%253Cxsl%253Avalue-of%2520select%253D%2522system-property(%2527xsl%253Avendor%2527)%2522%252F%253E%253C!--&uiCulture=ar (and other several URLs...)* -* *[GET] — https://localhost:44349/Abp/ApplicationConfigurationScript* +* *[GET] — https://localhost:44348 (and other several URLs...)* +* *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=ar&returnUrl=%2FAccount%2FForgotPassword%3FreturnUrl%3D%2522%252F%253E%253Cxsl%253Avalue-of%2520select%253D%2522system-property(%2527xsl%253Avendor%2527)%2522%252F%253E%253C!--&uiCulture=ar (and other several URLs...)* +* *[GET] — https://localhost:44348/Abp/ApplicationConfigurationScript* **Description:** -A cookie has been set without the secure flag, which means that the cookie can be accessed via unencrypted connections. +A cookie has been set without the `HttpOnly` flag, which means that the cookie can be accessed by JavaScript running in the browser. **Explanation:** The following alert is related to the next alert. Therefore, to understand this alert, you can take a look at the next alert: _Cookie Without Secure Flag [Risk: Low]_ -### Cookie Without Secure Flag [Risk: Low] — Positive (No need for a fix) +### Cookie Without Secure Flag [Risk: Low] — Application/Deployment Review Required -* *[GET] — https://localhost:44349 (and other several URLs...)* -* *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ar&returnUrl=%2F%3Fpage%3D% (same url with different query parameters...)* +* *[GET] — https://localhost:44348 (and other several URLs...)* +* *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=ar&returnUrl=%2F%3Fpage%3D% (same url with different query parameters...)* -**Description:** A cookie has been set without the secure flag, which means that the cookie can be accessed via unencrypted connections. The following cookies don't have an `httponly` flag. +**Description:** A cookie has been set without the `Secure` flag, which means that the cookie can be sent over unencrypted HTTP connections. -* `XSRF-TOKEN` (Anti CSRF token cookie) -* `.AspNetCore.Culture` (ASP.NET Core culture cookie) +The ZAP report includes JavaScript-readable and UI preference cookies in this category. The local HTTPS validation found `XSRF-TOKEN` with `Secure=true` and `SameSite=None`, and found UI/local antiforgery cookies without `Secure` on localhost. Review the final production deployment and reverse proxy settings to ensure security-sensitive cookies are only sent over HTTPS. **Explanation:** -All the pages that are setting the `XSRF-TOKEN` and `.AspNetCore.Culture` cookies in the HTTP response are reported as "No `HttpOnly` Flag" vulnerability. This is a **positive-alert**. +All the pages that are setting the `XSRF-TOKEN` and `.AspNetCore.Culture` cookies in the HTTP response can be reported in cookie flag alerts. This is expected for cookies that must be read by client-side code, but production deployments should still verify the `Secure` and `SameSite` attributes for each cookie. > **Note for IDS4 users**: The `idsrv.session` cookie is being used in IDS4 and after ABP 6.x, ABP switched to OpenIddict ([github.com/abpframework/abp/issues/7221](https://github.com/abpframework/abp/issues/7221)). Therefore, this cookie is not being used in the current startup templates and you can ignore this note if you have created your application after v6.0+. However, if you are still using Identity Server 4, there is an issue related to the `idsrv.session` cookie, it cannot be set as `HttpOnly`; you can see the related thread at its own repository: [github.com/IdentityServer/IdentityServer4/issues/3873](https://github.com/IdentityServer/IdentityServer4/issues/3873) @@ -233,10 +176,10 @@ The related issue for this alert can be found at [github.com/abpframework/abp/is ### Cookie with SameSite Attribute None [Risk: Low] — Positive (No need for a fix) -* *[GET] — https://localhost:44349 (and other several URLs...)* -* *[GET] — https://localhost:44349/Abp/ApplicationConfigurationScript* -* *[GET] — https://localhost:44349/Account/ForgotPassword (and there are several URLs)* -* *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ar&returnUrl=%2F%3Fpage%3D%252FAccount%252F%7E%252FAccount%252FLogin&uiCulture=a (and other several URLs...)* +* *[GET] — https://localhost:44348 (and other several URLs...)* +* *[GET] — https://localhost:44348/Abp/ApplicationConfigurationScript* +* *[GET] — https://localhost:44348/Account/ForgotPassword (and there are several URLs)* +* *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=ar&returnUrl=%2F%3Fpage%3D%252FAccount%252F%7E%252FAccount%252FLogin&uiCulture=a (and other several URLs...)* **Description:** @@ -250,7 +193,7 @@ Ensure that the `SameSite` attribute is set to either `lax` or ideally `strict` ### Cookie without `SameSite` Attribute [Risk: Low] — Positive (No need for a fix) -* *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ar&returnUrl=%2F&uiCulture=ar _(and other several URLs with different query parameters...)_* +* *[GET] — https://localhost:44348/Abp/Languages/Switch?culture=ar&returnUrl=%2F&uiCulture=ar _(and other several URLs with different query parameters...)_* **Description:** @@ -262,17 +205,12 @@ Ensure that the `SameSite` attribute is set to either `lax` or ideally `strict` -### Strict-Transport-Security Header Not Set [Risk: Low] - False Positive +### Strict-Transport-Security Header Not Set [Risk: Low] - Production/Deployment Setting -- *[DELETE] — https://localhost:44349/api/feature-management/features?providerName=E&providerKey=49dfb08f-f5ed-0b61-8d37-3a0fc6b61679* -- *[DELETE] — https://localhost:44349/api/identity/claim-types/4c580525-c08f-9280-f729-3a0fc6b9c3fa* -- *[DELETE] — https://localhost:44349/api/account/sessions/de2f8683-f8f4-d3bb-6b7e-3a138af89d1f* -- *[DELETE] — https://localhost:44349/api/language-management/languages/6b311a44-65bd-14ea-1a21-3a0e778b41d5* -- *[DELETE] — https://localhost:44349/api/saas/tenants/c77b1554-5837-3303-9983-3a0e77824bb3* -- *[DELETE] — https://localhost:44349/api/openiddict/scopes?id=af5a66e2-7cbb-cf69-7301-3a0fc6bb0ebf* -- *[GET] — https://localhost:44349/* -- *[GET] — https://localhost:44349/Abp/ApplicationConfigurationScript* -- *[GET] — https://localhost:44349/Abp/ApplicationLocalizationScript?cultureName=zh-Hant* +- *[GET] — https://localhost:44348/* +- *[GET] — https://localhost:44348/Abp/ApplicationConfigurationScript* +- *[GET] — https://localhost:44348/Abp/ApplicationLocalizationScript?cultureName=zh-Hant* +- *[DELETE] — https://localhost:44348/api/feature-management/features?providerName=E&providerKey=...* - other URLS... **Description**: @@ -285,14 +223,13 @@ Enabling HSTS on production. **Explanation**: -This vulnerability was reported as a positive alert because the application ran in `Development` mode. We enable HSTS on `Production` mode as can be seen in the image below, therefore this is a **false-positive** alert. +This alert is production/deployment dependent. The generated MVC template calls `UseHsts()` when the environment is not `Development`, but ASP.NET Core does not emit HSTS for localhost. Verify that HSTS is enabled and reaches the browser in the final production hosting topology. ![HSTS](../images/pen-test-hsts.png) ### Timestamp Disclosure - Unix [Risk: Low] - False Positive -- *[GET] — https://localhost:44349/libs/zxcvbn/zxcvbn.js?=* -- *[GET] — https://localhost:44349/libs/sweetalert2/sweetalert2.all.min.js?=* +- *[GET] — https://localhost:44348/libs/zxcvbn/zxcvbn.js?_v=638362269519660000* **Description**: @@ -306,13 +243,12 @@ Manually confirm that the timestamp data is not sensitive, and that the data can This vulnerability was reported as a positive alert, because ABP uses the [zxcvbn](https://github.com/dropbox/zxcvbn) library for [password complexity indicators](../framework/ui/angular/password-complexity-indicator-component.md). This library is one of the most used password strength estimator and it does not disclosure any sensitive data related to web server's timestamp and therefore it's a **false-positive** alert. -### X-Content-Type-Options Header Missing [Risk: Low] - Positive (Fixed) +### X-Content-Type-Options Header Missing [Risk: Low] - Needs Review for Static/Deployment Paths -- *[GET] — https://localhost:44349/client-proxies/account-proxy.js?_v=638550091940000000 (and other client-proxies related URLs...)* -- *[GET] — https://localhost:44349/favicon.svg* -- *[GET] — https://localhost:44349/images/getting-started/bg-01.png* (and other image URLs...) -- *[GET] — https://localhost:44349/global-styles.css?_v=638556076064360335* -- *[GET] — https://localhost:44349/libs/@fortawesome/fontawesome-free/css/all.css?_v=%5CWEB-INF%5Cweb.xml (other several URLs...)* +- *[GET] — https://localhost:44348/client-proxies/account-proxy.js?_v=639159181980000000 (and other client-proxies related URLs...)* +- *[GET] — https://localhost:44348/favicon.svg* +- *[GET] — https://localhost:44348/LeptonX/images/login-pages/login-bg-img-dark.svg* +- *[GET] — https://localhost:44348/libs/abp/aspnetcore-mvc-ui-theme-shared/authentication-state/authentication-state-listener.js?_v=639158976700000000* - other URLs... **Description**: @@ -327,6 +263,6 @@ If possible, ensure that the end user uses a standards-compliant and modern web **Explanation**: -The `X-Content-Type-Options` header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured. This headeer is not strictly required, but it is highly recommended for security reasons. While modern browsers have improved security features, you can still set this header for ensuring the security of web applications. +The `X-Content-Type-Options` header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured. This header is not strictly required, but it is highly recommended for security reasons. While modern browsers have improved security features, you can still set this header for ensuring the security of web applications. -You can add the [ABP's Security Header Middleware](../framework/ui/mvc-razor-pages/security-headers.md#security-headers-middleware) into the request pipeline to set the `X-Content-Type-Options` as *no-sniff*. Also, this middleware adds other pre-defined security headers to your application, including `X-XSS-Protection`, `X-Frame-Options` and `Content-Security-Policy` (if it's enabled). Read [Security Headers](../framework/ui/mvc-razor-pages/security-headers.md) documentation for more info. +The fresh local validation confirmed that [ABP's Security Header Middleware](../framework/ui/mvc-razor-pages/security-headers.md#security-headers-middleware) emits `X-Content-Type-Options: nosniff` for checked MVC pages and scripts. Since the ZAP report still listed missing headers for several client proxy and static asset URLs, verify static-file handling, proxy/CDN behavior, and any custom middleware ordering in the final application. This middleware also adds other pre-defined security headers, including `X-XSS-Protection`, `X-Frame-Options` and `Content-Security-Policy` (if it's enabled). Read [Security Headers](../framework/ui/mvc-razor-pages/security-headers.md) documentation for more info. diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index c07b95d054..f6c9e276f2 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -9,7 +9,18 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. -## 3.0.3 (2026-05-20) Latest +## 3.0.4 (2026-06-03) Latest + +* Blazor UI Selector: Added a built-in choice between MudBlazor and Blazorise for Blazor solution templates +* React Native Updates: Upgraded Expo to the latest version, improved keyboard handling, and ensured UI consistency +* Linux Support Enhancements: Improved system tool detection, terminal launching, and added full Linux publish/runtime support +* React Admin Security: Secured the React Admin Console sidebar menu by enforcing permission-based access gating +* Mobile Documentation: Added comprehensive setup guides and rules for React Native mobile templates +* Angular Microservice Fix: Resolved missing audit logging configurations and imports in microservice templates +* Modern Templates Licensing: Updated Modern architecture templates to be available exclusively for commercial/paid tiers +* Code & UI Stability: Fixed TypeScript compilation errors in React templates and resolved various minor system bugs + +## 3.0.3 (2026-05-20) * AI Agent Upgrades: Added browser automation tools and overall performance fixes * React Language Fix: Fixed language and localization settings being ignored in React templates diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index 14098122c2..d254004d4f 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -9,9 +9,10 @@ This document provides a general overview of the relationship between various versions of ABP Studio and the ABP version of the [ABP Solution Templates](../solution-templates/index.md) used when generating a new solution. Each version of ABP Studio is configured to create solutions with a specific ABP version. However, once a solution is created, you can easily update it to the latest version of ABP via ABP Studio or [ABP CLI](../cli/index.md#update). -| **ABP Studio Version** | **ABP Version of Startup Template** | +| **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| -| 3.0.3 | 10.4.0 | +| 3.0.4 | 10.4.1 | +| 3.0.3 | 10.4.0 | | 2.2.7 - 3.0.2 | 10.3.0 | | 2.2.5 - 2.2.6 | 10.2.0 | | 2.2.2 - 2.2.4 | 10.1.1 | diff --git a/docs/en/tutorials/mobile/react-native/index.md b/docs/en/tutorials/mobile/react-native/index.md index 5e97b6274a..db41615d98 100644 --- a/docs/en/tutorials/mobile/react-native/index.md +++ b/docs/en/tutorials/mobile/react-native/index.md @@ -1,13 +1,13 @@ ```json //[doc-seo] { - "Description": "Learn how to develop a mobile application using React Native with the ABP Framework. Build the Acme.BookStore mobile UI on top of the modernized ABP React Native template (NativeWind v4 + Bottom Tab navigation)." + "Description": "Learn how to develop a mobile application using React Native with the ABP Framework. Build the Acme.BookStore mobile UI on top of the modernized ABP React Native template (NativeWind v4 + Bottom Tab navigation)." } ``` # Mobile Application Development Tutorial - React Native -The React Native mobile option is *available for* ***Team*** *or higher licenses*. If you don't have a commercial license, follow this article by downloading the source code of the sample application linked below. +The React Native mobile option is _available for_ **_Team_** _or higher licenses_. If you don't have a commercial license, follow this article by downloading the source code of the sample application linked below. ## About This Tutorial @@ -18,6 +18,16 @@ The React Native mobile option is *available for* ***Team*** *or higher licenses - The mobile template was modernized in 2026: it now uses **NativeWind v4** (Tailwind CSS for React Native) for styling, **Bottom Tab navigation** by default, and the **Redux Toolkit** store with hook-based access (`useSelector` / `useDispatch`). The `connectToRedux` HOC, the `DrawerNavigator`, and the legacy `DataList`/`AbpSelect` components from earlier versions no longer ship with the template — this tutorial walks through building the new equivalents. - Before starting, please make sure that the [React Native Development Environment](../../../framework/ui/react-native/index.md) is ready on your machine. +## Running the Application + +Before implementing UI changes, run the `Acme.BookStore` mobile application and verify that login works: + +1. Open the solution in **ABP Studio** and run the **Initialize Solution** task once (creates SSL certificates and other one-time setup). +2. For browser testing, follow [Running on Web](../../../framework/ui/react-native/running-on-web.md) — start the **Default** run profile; ABP Studio opens the app at **`https://localhost:8443`**. +3. For an Android emulator or iOS simulator, follow [Running on Device](../../../framework/ui/react-native/running-on-device.md). The sample is a **Pro, non-tiered Monolith** solution — switch to the **MobileEmulator** run profile, update `react-native/Environment.ts`, and start the profile (or run `yarn tunnel:api` manually). For **Tiered** or **Microservice** solutions, use the manual backend steps in that guide instead. + +See the [React Native overview](../../../framework/ui/react-native/index.md) for environment setup and project creation. + ## Download the Source Code You can use the following link to download the source code of the application described in this article: @@ -1059,6 +1069,7 @@ function CreateUpdateBookForm({ submit, book, authors }: CreateUpdateBookFormPro }} /> + {/* Publish date modal */} setDateModalVisible(false)}> setDateModalVisible(false)} className="flex-1 bg-black/50 items-center justify-center px-6"> @@ -1239,7 +1250,6 @@ function CreateUpdateAuthorScreen({ navigation, route }: CreateUpdateAuthorScree })(); return () => { cancelled = true; }; }, [authorId, dispatch]); - const submit = async (data: AuthorFormValues) => { dispatch(LoadingActions.start({ key: 'save' })); try { diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs index fe0a0e09f9..3d6cd86941 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs @@ -7,10 +7,16 @@ namespace Volo.Abp.BlobStoring; public class BlobContainerConfiguration { + private Type? _providerType; + /// /// The provider to be used to store BLOBs of this container. /// - public Type? ProviderType { get; set; } + public Type? ProviderType + { + get => _providerType ?? _fallbackConfiguration?.ProviderType; + set => _providerType = value; + } /// /// Indicates whether this container is multi-tenant or not. @@ -37,6 +43,20 @@ public class BlobContainerConfiguration _properties = new Dictionary(); } + /// + /// Returns the naming normalizers in effect for this container, inheriting from the fallback + /// configuration only when this container has none and does not override . + /// + public IEnumerable GetEffectiveNamingNormalizers() + { + if (NamingNormalizers.Count == 0 && _providerType == null && _fallbackConfiguration != null) + { + return _fallbackConfiguration.GetEffectiveNamingNormalizers(); + } + + return NamingNormalizers; + } + public T? GetConfigurationOrDefault(string name, T? defaultValue = default) { return (T?)GetConfigurationOrNull(name, defaultValue); diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobNormalizeNamingService.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobNormalizeNamingService.cs index 397b48ca0c..cd1ae931b5 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobNormalizeNamingService.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobNormalizeNamingService.cs @@ -19,15 +19,15 @@ public class BlobNormalizeNamingService : IBlobNormalizeNamingService, ITransien string? containerName, string? blobName) { - - if (!configuration.NamingNormalizers.Any()) + var normalizerTypes = configuration.GetEffectiveNamingNormalizers(); + if (!normalizerTypes.Any()) { return new BlobNormalizeNaming(containerName, blobName); } using (var scope = ServiceProvider.CreateScope()) { - foreach (var normalizerType in configuration.NamingNormalizers) + foreach (var normalizerType in normalizerTypes) { var normalizer = scope.ServiceProvider .GetRequiredService(normalizerType) @@ -43,21 +43,11 @@ public class BlobNormalizeNamingService : IBlobNormalizeNamingService, ITransien public string NormalizeContainerName(BlobContainerConfiguration configuration, string containerName) { - if (!configuration.NamingNormalizers.Any()) - { - return containerName; - } - return NormalizeNaming(configuration, containerName, null).ContainerName!; } public string NormalizeBlobName(BlobContainerConfiguration configuration, string blobName) { - if (!configuration.NamingNormalizers.Any()) - { - return blobName; - } - return NormalizeNaming(configuration, null, blobName).BlobName!; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs index b8af22ff19..25f9268ff2 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -75,6 +75,7 @@ public class AbpCliCoreModule : AbpModule options.Commands[CreateMigrationAndRunMigratorCommand.Name] = typeof(CreateMigrationAndRunMigratorCommand); options.Commands[InstallLibsCommand.Name] = typeof(InstallLibsCommand); options.Commands[CleanCommand.Name] = typeof(CleanCommand); + options.Commands[CleanLogsCommand.Name] = typeof(CleanLogsCommand); options.Commands[CliCommand.Name] = typeof(CliCommand); options.Commands[ClearDownloadCacheCommand.Name] = typeof(ClearDownloadCacheCommand); options.Commands[RecreateInitialMigrationCommand.Name] = typeof(RecreateInitialMigrationCommand); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanLogsCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanLogsCommand.cs new file mode 100644 index 0000000000..91d1b07d1f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanLogsCommand.cs @@ -0,0 +1,57 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Volo.Abp.Cli.Args; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands; + +public class CleanLogsCommand : IConsoleCommand, ITransientDependency +{ + public const string Name = "clean-logs"; + + public ILogger Logger { get; set; } + + public CleanLogsCommand(ILogger logger) + { + Logger = logger; + } + + public Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + var logsEntries = Directory.EnumerateDirectories(Directory.GetCurrentDirectory(), "Logs", SearchOption.AllDirectories); + + Logger.LogInformation($"Removing 'Logs' files..."); + foreach (var path in logsEntries) + { + var files = Directory.GetFiles(path, "*logs.txt"); + + foreach (var file in files) + { + Logger.LogInformation($"Deleting: {file}"); + File.Delete(file); + } + } + Logger.LogInformation("Logs cleaned successfully!"); + return Task.CompletedTask; + } + + public string GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp clean-logs"); + sb.AppendLine(""); + sb.AppendLine("See the documentation for more info: https://abp.io/docs/latest/cli"); + + return sb.ToString(); + } + + public static string GetShortDescription() + { + return "Delete all *logs.txt files in the current folder and its subfolders."; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs index 737bcb3c2f..9c0f1fed13 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs @@ -30,6 +30,11 @@ namespace Volo.Abp.Application; )] public class AbpDddApplicationModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AbpDynamicSortingGuard.Install(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs new file mode 100644 index 0000000000..2f67547b0b --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs @@ -0,0 +1,154 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using Volo.Abp.Validation; + +[assembly: InternalsVisibleTo("Volo.Abp.Ddd.Application.Tests")] + +namespace Volo.Abp.Application.Services; + +/// +/// Framework infrastructure. Hooks so +/// every OrderBy / ThenBy expression built from a user-supplied sorting string is +/// constrained to plain property or field access. Methods, comparisons, ternaries +/// and constants in the sort key are rejected with . +/// Property-bag / shadow-property access through a constant string indexer +/// (e.g. it["Prop"], Data["Prop"]) is treated like plain property +/// access and allowed, because it is the canonical way to sort dynamically-mapped +/// entities. The guard assumes the matching indexer getter behaves like a property +/// getter; defining an indexer that performs IO or mutates state will expose those +/// effects through sorting. +/// +internal static class AbpDynamicSortingGuard +{ + private static readonly object InstallLock = new(); + private static Func? _activeOptimizer; + + public static void Install() + { + lock (InstallLock) + { + var current = ExtensibilityPoint.QueryOptimizer; + if (_activeOptimizer != null && ReferenceEquals(current, _activeOptimizer)) + { + return; + } + + var previous = current; + _activeOptimizer = expression => + { + new OrderByMethodVisitor().Visit(expression); + return previous != null ? previous(expression) : expression; + }; + ExtensibilityPoint.QueryOptimizer = _activeOptimizer; + } + } + + internal static void Reset() + { + lock (InstallLock) + { + if (ReferenceEquals(ExtensibilityPoint.QueryOptimizer, _activeOptimizer)) + { + ExtensibilityPoint.QueryOptimizer = null; + } + _activeOptimizer = null; + } + } + + private sealed class OrderByMethodVisitor : ExpressionVisitor + { + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.DeclaringType == typeof(Queryable) && + IsOrderByMethod(node.Method.Name) && + node.Arguments.Count >= 2 && + node.Arguments[1] is UnaryExpression { Operand: LambdaExpression lambda }) + { + new PropertyOnlySelectorVisitor().Visit(lambda.Body); + } + + return base.VisitMethodCall(node); + } + + private static bool IsOrderByMethod(string name) + { + return name == nameof(Queryable.OrderBy) + || name == nameof(Queryable.OrderByDescending) + || name == nameof(Queryable.ThenBy) + || name == nameof(Queryable.ThenByDescending); + } + } + + private sealed class PropertyOnlySelectorVisitor : ExpressionVisitor + { + private const string Message = "Sorting expression is not supported."; + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + // Allow property-bag / shadow-property access through a constant string + // indexer (it["Prop"], Data["Prop"], mainEntity["Prop"], ...). The + // constant key cannot smuggle in an arbitrary method invocation, so this + // does not widen the injection surface the guard protects against; it + // treats the indexer access like ordinary property access. + if (IsConstantStringIndexer(node)) + { + if (node.Object != null) + { + Visit(node.Object); + } + + return node; + } + + throw new AbpValidationException(Message); + } + + protected override Expression VisitBinary(BinaryExpression node) + => throw new AbpValidationException(Message); + + protected override Expression VisitConditional(ConditionalExpression node) + => throw new AbpValidationException(Message); + + protected override Expression VisitConstant(ConstantExpression node) + => throw new AbpValidationException(Message); + + private static bool IsConstantStringIndexer(MethodCallExpression node) + { + // Must be an instance call with a single constant string argument. + if (node.Object == null + || node.Arguments.Count != 1 + || node.Arguments[0] is not ConstantExpression { Value: string }) + { + return false; + } + + // And the resolved method must be the get accessor of a real string-keyed + // indexer (a special-name property getter), not an arbitrary method that + // merely shares the compiler-generated "get_Item" name. This keeps the + // guard's "no method calls" rule intact for everything except indexers. + var method = node.Method; + var parameters = method.GetParameters(); + if (!method.IsSpecialName + || method.IsStatic + || parameters.Length != 1 + || parameters[0].ParameterType != typeof(string)) + { + return false; + } + + return method.DeclaringType? + .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Any(property => + { + var indexParameters = property.GetIndexParameters(); + return indexParameters.Length == 1 + && indexParameters[0].ParameterType == typeof(string) + && property.GetGetMethod(nonPublic: true) == method; + }) == true; + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs index 0035cfbe36..48c4c6948f 100644 --- a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs @@ -29,10 +29,22 @@ public class AbpBlobStoringOptions_Tests : AbpBlobStoringTestBase } [Fact] - public void Should_Fallback_To_Default_Configuration_If_Not_Specialized() + public void Should_Fallback_To_Default_ProviderType_When_Not_Explicitly_Configured() { var config = _configurationProvider.Get(); config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + config.IsMultiTenant.ShouldBeFalse(); config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); } + + [Fact] + public void Should_Resolve_Fallback_Chain_Through_Configuration_Provider() + { + var testContainer1Config = _configurationProvider.Get(); + testContainer1Config.IsMultiTenant.ShouldBeTrue(); + + var testContainer3Config = _configurationProvider.Get(); + testContainer3Config.IsMultiTenant.ShouldBeFalse(); + testContainer3Config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + } } diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs index a6746a9480..93523a8c88 100644 --- a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs @@ -36,6 +36,10 @@ public class AbpBlobStoringTestModule : AbpModule { container.SetConfiguration("TestConfig2", "TestValue2"); container.ProviderType = typeof(FakeBlobProvider2); + }) + .Configure(container => + { + container.IsMultiTenant = false; }); }); } diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerConfiguration_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerConfiguration_Tests.cs new file mode 100644 index 0000000000..f47faa1f25 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerConfiguration_Tests.cs @@ -0,0 +1,62 @@ +using System.Linq; +using Shouldly; +using Volo.Abp.BlobStoring.Fakes; +using Xunit; + +namespace Volo.Abp.BlobStoring; + +public class BlobContainerConfiguration_Tests +{ + [Fact] + public void Should_Override_Default_Container_ProviderType_When_Named_Container_Has_Its_Own() + { + var defaultConfig = new BlobContainerConfiguration(); + defaultConfig.ProviderType = typeof(FakeBlobProvider1); + + var namedConfig = new BlobContainerConfiguration(defaultConfig); + namedConfig.ProviderType = typeof(FakeBlobProvider2); + + namedConfig.ProviderType.ShouldBe(typeof(FakeBlobProvider2)); + defaultConfig.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + } + + [Fact] + public void Should_Inherit_Default_Container_NamingNormalizers_When_Provider_Also_Inherited() + { + var defaultConfig = new BlobContainerConfiguration(); + defaultConfig.ProviderType = typeof(FakeBlobProvider1); + defaultConfig.NamingNormalizers.Add(); + + var namedConfig = new BlobContainerConfiguration(defaultConfig); + + namedConfig.GetEffectiveNamingNormalizers().ShouldContain(typeof(FakeNamingNormalizer)); + namedConfig.NamingNormalizers.ShouldBeEmpty(); + } + + [Fact] + public void Should_Not_Inherit_NamingNormalizers_When_Named_Container_Has_Its_Own_ProviderType() + { + var defaultConfig = new BlobContainerConfiguration(); + defaultConfig.ProviderType = typeof(FakeBlobProvider1); + defaultConfig.NamingNormalizers.Add(); + + var namedConfig = new BlobContainerConfiguration(defaultConfig); + namedConfig.ProviderType = typeof(FakeBlobProvider2); + + namedConfig.GetEffectiveNamingNormalizers().ShouldBeEmpty(); + } + + [Fact] + public void Should_Override_Default_Container_NamingNormalizers_When_Named_Container_Has_Its_Own() + { + var defaultConfig = new BlobContainerConfiguration(); + defaultConfig.NamingNormalizers.Add(); + + var namedConfig = new BlobContainerConfiguration(defaultConfig); + namedConfig.NamingNormalizers.Add(); + + var effective = namedConfig.GetEffectiveNamingNormalizers().ToList(); + effective.ShouldContain(typeof(AnotherFakeNamingNormalizer)); + effective.ShouldNotContain(typeof(FakeNamingNormalizer)); + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/AnotherFakeNamingNormalizer.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/AnotherFakeNamingNormalizer.cs new file mode 100644 index 0000000000..60e53b5c2a --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/AnotherFakeNamingNormalizer.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.BlobStoring.Fakes; + +public class AnotherFakeNamingNormalizer : IBlobNamingNormalizer +{ + public string NormalizeContainerName(string containerName) + { + return containerName; + } + + public string NormalizeBlobName(string blobName) + { + return blobName; + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeNamingNormalizer.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeNamingNormalizer.cs new file mode 100644 index 0000000000..a5a32a0d6d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeNamingNormalizer.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.BlobStoring.Fakes; + +public class FakeNamingNormalizer : IBlobNamingNormalizer +{ + public string NormalizeContainerName(string containerName) + { + return containerName; + } + + public string NormalizeBlobName(string blobName) + { + return blobName; + } +} diff --git a/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.abppkg b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.abppkg new file mode 100644 index 0000000000..64c1552e37 --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.abppkg @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} diff --git a/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.csproj b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.csproj new file mode 100644 index 0000000000..1836c9133a --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo.Abp.Ddd.Application.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + net10.0 + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestBase.cs b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestBase.cs new file mode 100644 index 0000000000..1121eaaef9 --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Modularity; +using Volo.Abp.Testing; + +namespace Volo.Abp.Application; + +public abstract class AbpDddApplicationTestBase : AbpIntegratedTest +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } +} diff --git a/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestModule.cs b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestModule.cs new file mode 100644 index 0000000000..ef49b36d76 --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/AbpDddApplicationTestModule.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Autofac; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Application; + +[DependsOn(typeof(AbpAutofacModule))] +[DependsOn(typeof(AbpDddApplicationModule))] +[DependsOn(typeof(AbpExceptionHandlingModule))] +public class AbpDddApplicationTestModule : AbpModule +{ +} diff --git a/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/Services/AbpDynamicSortingGuard_Tests.cs b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/Services/AbpDynamicSortingGuard_Tests.cs new file mode 100644 index 0000000000..60f5dcd55a --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Application.Tests/Volo/Abp/Application/Services/AbpDynamicSortingGuard_Tests.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Validation; +using Xunit; + +namespace Volo.Abp.Application.Services; + +public class AbpDynamicSortingGuard_Tests : AbpDddApplicationTestBase +{ + private readonly IQueryable _users; + + public AbpDynamicSortingGuard_Tests() + { + _users = new List + { + new() { Name = "alice", Age = 30, PasswordHash = "AQAAhash_alice", Tenant = new FakeTenant { Name = "acme" } }, + new() { Name = "bob", Age = 25, PasswordHash = "BQAAhash_bob", Tenant = new FakeTenant { Name = "beta" } }, + new() { Name = "carl", Age = 40, PasswordHash = "CQAAhash_carl", Tenant = new FakeTenant { Name = "corp" } }, + }.AsQueryable(); + } + + [Theory] + [InlineData("Name")] + [InlineData("Name desc")] + [InlineData("Name asc, PasswordHash desc")] + [InlineData("Age desc")] // value type → EF/Dynamic.Core wraps selector in Convert(MemberAccess, object) + [InlineData("Tenant.Name")] // chained MemberAccess + [InlineData("Tenant.Name desc, Age asc")] // mixed chained + value-type, multi-column + [InlineData("Name.Length desc")] // Length is a property getter, not a method call + public void Should_Accept_Plain_Property_Sorting(string sorting) + { + Should.NotThrow(() => _users.OrderBy(sorting).ToList()); + } + + [Theory] + [InlineData("PasswordHash.Substring(0,1) desc")] + [InlineData("PasswordHash.StartsWith(\"A\") desc")] + [InlineData("PasswordHash.Contains(\"hash\") desc")] + [InlineData("Name asc, PasswordHash.Substring(0,1) desc")] // multi-column with attack in 2nd + public void Should_Reject_Method_Call_On_Property(string sorting) + { + Should.Throw(() => _users.OrderBy(sorting).ToList()) + .Message.ShouldBe("Sorting expression is not supported."); + } + + [Theory] + [InlineData("(PasswordHash == \"AQAA\") desc")] + [InlineData("(PasswordHash > \"M\") desc")] + [InlineData("(PasswordHash != \"AQAA\") asc")] + public void Should_Reject_Binary_Expressions(string sorting) + { + Should.Throw(() => _users.OrderBy(sorting).ToList()) + .Message.ShouldBe("Sorting expression is not supported."); + } + + [Fact] + public void Should_Not_Affect_Where_Expressions() + { + // The guard only inspects Queryable.OrderBy/ThenBy nodes. Where with the same + // sub-expression is left alone (Where is a separate vulnerability class). + Should.NotThrow(() => _users.Where("PasswordHash.StartsWith(\"A\")").ToList()); + } + + [Fact] + public void Install_Chains_Existing_QueryOptimizer() + { + // Reset guard state so Install() actually re-installs and exercises the + // `previous != null ? previous(expression) : expression` branch. + AbpDynamicSortingGuard.Reset(); + try + { + var preExistingFired = false; + ExtensibilityPoint.QueryOptimizer = e => + { + preExistingFired = true; + return e; + }; + + AbpDynamicSortingGuard.Install(); + + _users.OrderBy("Name").ToList(); + preExistingFired.ShouldBeTrue(); + } + finally + { + // Leave the AppDomain with a single-layer guard. Reset() clears whatever + // we wrapped in this test; Install() then puts a fresh guard on top of + // an empty QueryOptimizer — never double-wraps an existing guard. + AbpDynamicSortingGuard.Reset(); + AbpDynamicSortingGuard.Install(); + } + } + + [Fact] + public void Install_Reinstalls_When_QueryOptimizer_Was_Replaced() + { + // Simulate someone (e.g. a test teardown, another module) overwriting our + // optimizer. The next Install() must detect the mismatch and wrap again. + try + { + ExtensibilityPoint.QueryOptimizer = e => e; // not our wrapper + + AbpDynamicSortingGuard.Install(); + + // Guard must be active again — attack payload still gets rejected. + Should.Throw(() => + _users.OrderBy("PasswordHash.Substring(0,1) desc").ToList()); + } + finally + { + AbpDynamicSortingGuard.Reset(); + AbpDynamicSortingGuard.Install(); + } + } + + [Fact] + public void Should_Validate_Explicit_ThenBy_Selectors() + { + // ThenBy(string) is called directly by some app services, not only via multi-column OrderBy. + // The guard must reject unsafe expressions in ThenBy selectors as well. + Should.Throw(() => + _users.OrderBy("Name").ThenBy("PasswordHash.Substring(0,1)").ToList()); + } + + [Fact] + public void Should_Validate_ThenBy_With_Descending_Modifier() + { + // Dynamic.Core encodes direction in the sorting string (no separate ThenByDescending overload); + // make sure the guard still inspects the selector when desc is on the ThenBy column. + Should.Throw(() => + _users.OrderBy("Name").ThenBy("PasswordHash.Substring(0,1) desc").ToList()); + } + + [Theory] + [InlineData("(PasswordHash.Substring(0,1) == \"K\") desc")] // method + binary in one selector + [InlineData("(Name.Length > 5) desc")] // property getter + binary + [InlineData("(Age + 10) desc")] // binary arithmetic + [InlineData("(Age * Age) desc")] // binary arithmetic + [InlineData("(-Age) desc")] // unary negation wraps something; only Negate is allowed via base.VisitUnary, but the operand is a plain member so this passes — keep as accept check + public void Should_Reject_Compound_Or_Arithmetic_Expressions(string sorting) + { + // Note: the last case `(-Age) desc` actually passes — Unary(Negate(MemberAccess)) has no rejecting node. + // Filter accordingly. + if (sorting.Contains("(-")) + { + Should.NotThrow(() => _users.OrderBy(sorting).ToList()); + } + else + { + Should.Throw(() => _users.OrderBy(sorting).ToList()) + .Message.ShouldBe("Sorting expression is not supported."); + } + } + + [Theory] + [InlineData("Name.ToUpper().Substring(0,1) desc")] // chained method calls + [InlineData("PasswordHash.Substring(0,1).Length desc")] // method then property + public void Should_Reject_Chained_Method_Calls(string sorting) + { + Should.Throw(() => _users.OrderBy(sorting).ToList()); + } + + [Fact] + public void Should_Not_Interfere_With_DynamicCore_Validation_For_Empty_Or_Null_Sorting() + { + // Dynamic.Core itself rejects null / empty / whitespace ordering with ArgumentException + // before the QueryOptimizer is invoked. The guard must not turn those into + // AbpValidationException by accident. + Should.NotThrow(() => + { + try { _users.OrderBy((string)null!).ToList(); } catch (Exception ex) { ex.ShouldNotBeOfType(); } + try { _users.OrderBy("").ToList(); } catch (Exception ex) { ex.ShouldNotBeOfType(); } + try { _users.OrderBy(" ").ToList(); } catch (Exception ex) { ex.ShouldNotBeOfType(); } + }); + } + + [Fact] + public void Should_Validate_OrderBy_With_Args_Parameter() + { + // Dynamic.Core's OrderBy supports parameterized expressions through `@0`, `@1`, ... and args. + // The guard must validate the expanded selector, not the literal sorting string. + Should.Throw(() => + _users.OrderBy("PasswordHash.Substring(@0, 1) desc", 0).ToList()); + } + + [Fact] + public async Task Install_Is_Thread_Safe_Under_Concurrent_Calls() + { + // 50 concurrent Install() callers must not deadlock, throw, or leave the guard in a torn state. + AbpDynamicSortingGuard.Reset(); + try + { + await Task.WhenAll(Enumerable.Range(0, 50) + .Select(_ => Task.Run(AbpDynamicSortingGuard.Install))); + + // After concurrent installs, the guard must still reject attack payloads. + Should.Throw(() => + _users.OrderBy("PasswordHash.Substring(0,1) desc").ToList()); + } + finally + { + AbpDynamicSortingGuard.Reset(); + AbpDynamicSortingGuard.Install(); + } + } + + [Theory] + [InlineData("tr-TR")] // Turkish: uppercase 'I' is dotless, common locale pitfall for string ops + [InlineData("de-DE")] + [InlineData("ja-JP")] + [InlineData("en-US")] + public void Should_Reject_Method_Calls_Regardless_Of_CurrentCulture(string culture) + { + var saved = System.Globalization.CultureInfo.CurrentCulture; + try + { + System.Globalization.CultureInfo.CurrentCulture = new System.Globalization.CultureInfo(culture); + Should.Throw(() => + _users.OrderBy("PasswordHash.Substring(0,1) desc").ToList()); + } + finally + { + System.Globalization.CultureInfo.CurrentCulture = saved; + } + } + + [Theory] + [InlineData("Name DESC")] // upper-case direction modifier + [InlineData("name desc")] // lower-case property name (Dynamic.Core default case-insensitive lookup) + [InlineData("NAME ASC")] + public void Should_Accept_Property_Names_Regardless_Of_Casing(string sorting) + { + Should.NotThrow(() => _users.OrderBy(sorting).ToList()); + } + + [Theory] + [InlineData("passwordhash.substring(0,1) desc")] // lower-case attack + [InlineData("PASSWORDHASH.SUBSTRING(0,1) DESC")] // upper-case attack — method call detection cannot rely on case + public void Should_Reject_Method_Calls_Regardless_Of_Casing(string sorting) + { + Should.Throw(() => _users.OrderBy(sorting).ToList()); + } + + [Fact] + public void Should_Allow_Inherited_Property_Sorting() + { + var children = new List + { + new() { Name = "alpha", Age = 30, ExtraField = "x" }, + new() { Name = "beta", Age = 25, ExtraField = "y" }, + }.AsQueryable(); + + Should.NotThrow(() => children.OrderBy("Name desc, ExtraField asc").ToList()); + } + + [Theory] + [InlineData("Data[\"Score\"]")] // dictionary indexer with constant string key + [InlineData("Data[\"Score\"] desc")] + [InlineData("Name asc, Data[\"Score\"] desc")] // multi-column mixing plain + indexer + [InlineData("it[\"Score\"] desc")] // entity-level property-bag indexer (it["Prop"]) + public void Should_Accept_Constant_String_Indexer_Sorting(string sorting) + { + // Dynamically-mapped entities sort their shadow / property-bag members through a + // constant-string indexer; the guard must treat this as plain property access. + Should.NotThrow(() => FakeBagUsers().OrderBy(sorting).ToList()); + } + + [Theory] + [InlineData("Data[Name] desc")] // non-constant key (member access) + [InlineData("Data[PasswordHash.Substring(0,1)] desc")] // method call inside the key + public void Should_Reject_Non_Constant_Indexer_Sorting(string sorting) + { + Should.Throw(() => FakeBagUsers().OrderBy(sorting).ToList()) + .Message.ShouldBe("Sorting expression is not supported."); + } + + private static IQueryable FakeBagUsers() + { + return new List + { + new() { Name = "alice", PasswordHash = "AQAA", Data = { ["Score"] = 30 } }, + new() { Name = "bob", PasswordHash = "BQAA", Data = { ["Score"] = 25 } }, + new() { Name = "carl", PasswordHash = "CQAA", Data = { ["Score"] = 40 } }, + }.AsQueryable(); + } + + private class FakeUser + { + public string Name { get; set; } = ""; + public int Age { get; set; } + public string PasswordHash { get; set; } = ""; + public FakeTenant Tenant { get; set; } = new(); + } + + private class FakeChildUser : FakeUser + { + public string ExtraField { get; set; } = ""; + } + + private class FakeTenant + { + public string Name { get; set; } = ""; + } + + private class FakeBagUser + { + public string Name { get; set; } = ""; + public string PasswordHash { get; set; } = ""; + public Dictionary Data { get; set; } = new(); + + public object this[string key] => Data[key]; + } +} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json index 6ad936940b..59ac7ca94b 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1", - "@abp/highlight.js": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3", + "@abp/highlight.js": "~10.5.0-rc.3" } } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock index 0885eca199..a1f6b130b1 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock @@ -2,204 +2,204 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.1.tgz#ea71ca4a8bb3f50c75b841b2f5c48fe0d2229023" - integrity sha512-QBd0FWfChBVnRs+0AjLW/PyF+AdrL4IxrjNdJ+xbEzMhJ0nbv5Pjwy4p9+uEB/GSNzaPpE5M+5DR++TgbUEnPw== +"@abp/clipboard@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" + integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/highlight.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.1.tgz#5cad8083475b3f39bcc7675e0d568385e912e0a5" - integrity sha512-oSR7zv7coOoQKT/nLRn0Pm513RzUpEywo6fnOCd/wAJ8zzof0a2F4ju9vsMKaY7i174sGumX/dxWCsJ046+lJQ== +"@abp/highlight.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.3.tgz#ceffb929de26eec5903149a4eb3b64900ad9f7f4" + integrity sha512-crb7LwCqMzt0wX5hC4zuIVgML+9ljDJNjh6kCnf5N5LhWH8vsBf+8dwdcp8Jm9/XE/1rOtXN3938lkFo9F/0Qw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@highlightjs/cdn-assets" "~11.11.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.1.tgz#fb3e98a918417d5b938db3dc292f52f40b328ddf" - integrity sha512-pbw79D397KBBZabQ9vb3v0HjVr3FMvGo9LqVcxg+nuxTvuu2fPscBhyy6LQGFIMcbz2wGAqS88hGON36AldPRQ== +"@abp/prismjs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" + integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== dependencies: - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/core" "~10.5.0-rc.1" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.3" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json index 042485c422..ab16ef4cb2 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3" }, "devDependencies": {} } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock index 468b5f2a9d..e30ecb505c 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock @@ -2,203 +2,203 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.1.tgz#ea71ca4a8bb3f50c75b841b2f5c48fe0d2229023" - integrity sha512-QBd0FWfChBVnRs+0AjLW/PyF+AdrL4IxrjNdJ+xbEzMhJ0nbv5Pjwy4p9+uEB/GSNzaPpE5M+5DR++TgbUEnPw== +"@abp/clipboard@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" + integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.1.tgz#fb3e98a918417d5b938db3dc292f52f40b328ddf" - integrity sha512-pbw79D397KBBZabQ9vb3v0HjVr3FMvGo9LqVcxg+nuxTvuu2fPscBhyy6LQGFIMcbz2wGAqS88hGON36AldPRQ== +"@abp/prismjs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" + integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== dependencies: - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/core" "~10.5.0-rc.1" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.3" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/blogging/app/Volo.BloggingTestApp/package.json b/modules/blogging/app/Volo.BloggingTestApp/package.json index 13bc484a48..283ecc7146 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/package.json +++ b/modules/blogging/app/Volo.BloggingTestApp/package.json @@ -3,7 +3,7 @@ "name": "volo.blogtestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/blogging": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/blogging": "~10.5.0-rc.3" } } diff --git a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock index cef9a613a8..406e3c95da 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock +++ b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock @@ -2,229 +2,229 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/blogging@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-10.5.0-rc.1.tgz#f1b6186b01f52379ec4b5a2aa0f7757ab9b9c5d4" - integrity sha512-S+9itVhI4P7LtanUdfsOlZIcnK6GpFuqHpho5/uKYNKGoe9xxhUGyxr8SG+2PNNLyvcIzj/PK2QQOxb3jJk4fQ== +"@abp/blogging@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-10.5.0-rc.3.tgz#796d10b12a16d6b48ea5f9c53ea26a490199eef7" + integrity sha512-+7RV4CchmnzNPydsUgpr4bY3Er4SA0AiJhvoeTZQknBCo7oF7uY8OiqeVzWGj/iDodkreTN/x3QP1HjVQVOTXA== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" - "@abp/owl.carousel" "~10.5.0-rc.1" - "@abp/prismjs" "~10.5.0-rc.1" - "@abp/tui-editor" "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + "@abp/owl.carousel" "~10.5.0-rc.3" + "@abp/prismjs" "~10.5.0-rc.3" + "@abp/tui-editor" "~10.5.0-rc.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.1.tgz#ea71ca4a8bb3f50c75b841b2f5c48fe0d2229023" - integrity sha512-QBd0FWfChBVnRs+0AjLW/PyF+AdrL4IxrjNdJ+xbEzMhJ0nbv5Pjwy4p9+uEB/GSNzaPpE5M+5DR++TgbUEnPw== +"@abp/clipboard@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" + integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/owl.carousel@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-10.5.0-rc.1.tgz#cb62cfc04eea5e9f4a9f4b3fae6e8f63bbe25a6e" - integrity sha512-3ROMCVFU8oVZUOBX5pRUVFjk5DxPhN5twI1nBB8bo4vdTsYyzXcxV3pQ+6vnSwHeIZ2WoBx0EfA//MncmGKEAw== +"@abp/owl.carousel@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-10.5.0-rc.3.tgz#632dd138458040080128c539a4b98b5df7532c44" + integrity sha512-4XEc52wiN6sBFtt028pN8fz7Xjgx/23lBC6W4iTk2J2oeZXWkTWMszbIgMXEvAO5+wXzCPIbu1/kRLjgSp6gSQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" owl.carousel "^2.3.4" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.1.tgz#fb3e98a918417d5b938db3dc292f52f40b328ddf" - integrity sha512-pbw79D397KBBZabQ9vb3v0HjVr3FMvGo9LqVcxg+nuxTvuu2fPscBhyy6LQGFIMcbz2wGAqS88hGON36AldPRQ== +"@abp/prismjs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" + integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== dependencies: - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/core" "~10.5.0-rc.1" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.3" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/tui-editor@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.1.tgz#bb2bf9540b517e2b7b2a2d250761fbea691c4e4b" - integrity sha512-DjEMl4ZwjzNHSpOzzDjR/XMY0ZnTcn7IOcsJ60q5v1LaMmkqUKQekayyBLFqHNI339BwqSWMyPPB8Q56jpOD3A== +"@abp/tui-editor@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.3.tgz#3039865c149cea98dfedace000c4b392dc0680ed" + integrity sha512-SrwvvLH4GTJg9basCsu1cjmdy7BweDUAmZg0Zy8zZH1ry68FrV7AqGUujopssXkRooP6W/0VRpobbAXEyAAwzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" - "@abp/prismjs" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" + "@abp/prismjs" "~10.5.0-rc.3" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json index 2e01888be5..6917ce16f8 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json @@ -3,6 +3,6 @@ "name": "client-simulation-web", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock index 6c048b30be..820ecf8a71 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/angular/package.json b/modules/cms-kit/angular/package.json index 87d272e14c..eba25d9d5e 100644 --- a/modules/cms-kit/angular/package.json +++ b/modules/cms-kit/angular/package.json @@ -15,11 +15,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.1", - "@abp/ng.identity": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.tenant-management": "~10.5.0-rc.1", - "@abp/ng.theme.basic": "~10.5.0-rc.1", + "@abp/ng.account": "~10.5.0-rc.3", + "@abp/ng.identity": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.tenant-management": "~10.5.0-rc.3", + "@abp/ng.theme.basic": "~10.5.0-rc.3", "@angular/animations": "~10.0.0", "@angular/common": "~10.0.0", "@angular/compiler": "~10.0.0", diff --git a/modules/cms-kit/angular/projects/cms-kit/package.json b/modules/cms-kit/angular/projects/cms-kit/package.json index 4b22f8056d..ccb0d5c466 100644 --- a/modules/cms-kit/angular/projects/cms-kit/package.json +++ b/modules/cms-kit/angular/projects/cms-kit/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "^9.1.11", "@angular/core": "^9.1.11", - "@abp/ng.core": ">=10.5.0-rc.1", - "@abp/ng.theme.shared": ">=10.5.0-rc.1" + "@abp/ng.core": ">=10.5.0-rc.3", + "@abp/ng.theme.shared": ">=10.5.0-rc.3" }, "dependencies": { "tslib": "^2.0.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json index c123b8b764..f77ffb012e 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-identityserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock index 6c048b30be..820ecf8a71 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json index 1f101ce7d1..144454257f 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock index 6c048b30be..820ecf8a71 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index 3983fe0a19..596144bcf9 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/cms-kit": "10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/cms-kit": "10.5.0-rc.3" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index 936ba96878..a0a698dd92 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock @@ -2,298 +2,298 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.1.tgz#ea71ca4a8bb3f50c75b841b2f5c48fe0d2229023" - integrity sha512-QBd0FWfChBVnRs+0AjLW/PyF+AdrL4IxrjNdJ+xbEzMhJ0nbv5Pjwy4p9+uEB/GSNzaPpE5M+5DR++TgbUEnPw== +"@abp/clipboard@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" + integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" clipboard "^2.0.11" -"@abp/cms-kit.admin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-10.5.0-rc.1.tgz#adf84745faf332d8835de05c8b0d8ddd6d40887b" - integrity sha512-UkqX2aBEvvFqq6G+KOGQetprMqCKWy2VWEDnEzxsihis+bqKE86F6j8lIZDfwurs2sX5fnE/ucegCPK15Q/fFg== +"@abp/cms-kit.admin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-10.5.0-rc.3.tgz#74e8aed088a2721041e479ab305da1b42fd46207" + integrity sha512-+PNQ2PyhM5UYqyoNujG+HlpfTsOi/6nhbjOs1zH1ZBsGyUP5Rg144erkMYYQryouAj4I5kPvxV+VTtuqRzbl0A== dependencies: - "@abp/codemirror" "~10.5.0-rc.1" - "@abp/jstree" "~10.5.0-rc.1" - "@abp/markdown-it" "~10.5.0-rc.1" - "@abp/slugify" "~10.5.0-rc.1" - "@abp/tui-editor" "~10.5.0-rc.1" - "@abp/uppy" "~10.5.0-rc.1" + "@abp/codemirror" "~10.5.0-rc.3" + "@abp/jstree" "~10.5.0-rc.3" + "@abp/markdown-it" "~10.5.0-rc.3" + "@abp/slugify" "~10.5.0-rc.3" + "@abp/tui-editor" "~10.5.0-rc.3" + "@abp/uppy" "~10.5.0-rc.3" -"@abp/cms-kit.public@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-10.5.0-rc.1.tgz#e6df7500ac2668778a9152a0f39a230b135b9c4c" - integrity sha512-S/OFDjtpgWU9/Q61JXTtsmbzIlY+ikN4oHuEuq7w9yT7XpaZydgRG8VP02bnINmbkwLzX82TKepc6iswdLhHjQ== +"@abp/cms-kit.public@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-10.5.0-rc.3.tgz#d07b202a762b8f29106958ccb8f56bb6596c5ee0" + integrity sha512-pNjILNysxOOrJw8dqKmp813KsWI9nHhiGWYICQdisg18c2fy0TICcnWHCI1yW/yah9YybHkFRntC3DEKuPBM2A== dependencies: - "@abp/highlight.js" "~10.5.0-rc.1" - "@abp/star-rating-svg" "~10.5.0-rc.1" + "@abp/highlight.js" "~10.5.0-rc.3" + "@abp/star-rating-svg" "~10.5.0-rc.3" -"@abp/cms-kit@10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-10.5.0-rc.1.tgz#a6301bacfe0d305cb2e1edd20c731e09b11a80a7" - integrity sha512-Hk9PppjcsuDuOJI821DiHt85qwaFRxu6ZvcWG1n6f4sFZ6+0iBVLvU0nAP3s4YykaCPmQvikOpcDTJZ0OcDW5Q== +"@abp/cms-kit@10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-10.5.0-rc.3.tgz#addedc04c98661cf544491174d0d4cd4fb0fc5c6" + integrity sha512-HrNwsArlnSylt2wmO2ZhCdnS8DN7Y/qic6RdLwpKdhPqi/d1HovRGLZDVRDxrRPw8L52fEOtdBXJXs0KSLikRg== dependencies: - "@abp/cms-kit.admin" "~10.5.0-rc.1" - "@abp/cms-kit.public" "~10.5.0-rc.1" + "@abp/cms-kit.admin" "~10.5.0-rc.3" + "@abp/cms-kit.public" "~10.5.0-rc.3" -"@abp/codemirror@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-10.5.0-rc.1.tgz#71d6194e6ef4bcf759d6d1acbb208be0a7f0bc07" - integrity sha512-WKo+EEx4GlxOt4VREMOk3JF9nqtdyL+xgprFaQ82LWMeHPSEFg/JN9+hPnM2guoCYNI1aTxjhgSETalk4faQ2g== +"@abp/codemirror@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-10.5.0-rc.3.tgz#310e203f3d201db886341d4a725ef85198923add" + integrity sha512-NDxrxO3knC26GF+sVWmf8WmsjYPmWbpvu/b0/1qj3SXITQN5MNzURxvV4WpZ24dTSK4S/t8pg2VINgu5l7BbnA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@codemirror/lang-css" "^6.0.0" "@codemirror/lang-javascript" "^6.0.0" "@codemirror/state" "^6.0.0" "@codemirror/view" "^6.0.0" codemirror "^6.0.2" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/highlight.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.1.tgz#5cad8083475b3f39bcc7675e0d568385e912e0a5" - integrity sha512-oSR7zv7coOoQKT/nLRn0Pm513RzUpEywo6fnOCd/wAJ8zzof0a2F4ju9vsMKaY7i174sGumX/dxWCsJ046+lJQ== +"@abp/highlight.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.3.tgz#ceffb929de26eec5903149a4eb3b64900ad9f7f4" + integrity sha512-crb7LwCqMzt0wX5hC4zuIVgML+9ljDJNjh6kCnf5N5LhWH8vsBf+8dwdcp8Jm9/XE/1rOtXN3938lkFo9F/0Qw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@highlightjs/cdn-assets" "~11.11.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/jstree@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-10.5.0-rc.1.tgz#e3aa390625d3a0ba8fd024ba428651b968a5e2bb" - integrity sha512-GG1kSHtjgXQg+GNuViVevXdM7pY7eRt2sT+QdNBKmiVagglE3jgbVMI5eI/S7j0IeZJ146SU3T1W65CtWEQQqQ== +"@abp/jstree@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-10.5.0-rc.3.tgz#70b139caa5bb695a6596e270f277161dbd76be51" + integrity sha512-o1gAXT1A50e8RFUnZ9cDM6VkvSz73hrAV6ftPHj0cOjlEDSnWiHp4mjjLgk9ZHEDHRWhOiB/DK+Xxf5R7APSWg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jstree "^3.3.17" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/markdown-it@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-10.5.0-rc.1.tgz#cb0331d2e0def109d6989f5a6d95831db6135e42" - integrity sha512-YVlXYgpz+9F5YAs8TdqW42yYe8ttNvljBI8QQUFWpQi64RVt0X6O8beroHxTg4jzxtBH2Zh4dez4w0LlRsCynA== +"@abp/markdown-it@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-10.5.0-rc.3.tgz#5e1f2d069139b30628d11c2eed37fdaaed6b93c3" + integrity sha512-bgFsY3HkLqNF682Bxlhi9m/VwDNXq1D3UR9cXyP/ucaJNsHwyZuoAr3PK1NkhoBV3jjxsqE6ft8cQaH1xfLUXA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" markdown-it "^14.1.0" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.1.tgz#fb3e98a918417d5b938db3dc292f52f40b328ddf" - integrity sha512-pbw79D397KBBZabQ9vb3v0HjVr3FMvGo9LqVcxg+nuxTvuu2fPscBhyy6LQGFIMcbz2wGAqS88hGON36AldPRQ== +"@abp/prismjs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" + integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== dependencies: - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/core" "~10.5.0-rc.1" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.3" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/slugify@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-10.5.0-rc.1.tgz#7a30aa18c000c7b9712682c9473cb79d752fbea6" - integrity sha512-qICN8ZTxIPcCjpXcePJr52jAz8KULdGinl6DZLTvESAkh9gR2tFEqr/9lk023uZvglN/P0fZTofYZboSuTKjsA== +"@abp/slugify@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-10.5.0-rc.3.tgz#437ddb0b56c966a9a0f60ea641636685438b2cca" + integrity sha512-fsIEV6PD3tViuBLc0CGuUcK4sLjsxRB4yaGC2vOBd1evfBB3Dy39ZN0+OJOoJS8oFLBTpb32tIR2bz7xgw+2xg== dependencies: slugify "^1.6.6" -"@abp/star-rating-svg@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-10.5.0-rc.1.tgz#ef1246a7464bab4f4f191631825e1a00c872cd19" - integrity sha512-icQQrfNnMqLoMH+vpur7YXFtH1AdRSq3P8s5zn1mz8xwFLdGSMS1Cy3AtCqLCvG64ja+lBGdjvdoZaBmV8qNjw== +"@abp/star-rating-svg@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-10.5.0-rc.3.tgz#62990b9af71292f72531d16bf5aa6853f363d629" + integrity sha512-r0KFzqK0MO57YKY0vdVk8ST2T9PwXXpIUmGTT7wRf8d83pSmogEDxw8AYusb3tbi5A1YvzdtRZTglqYuLuCc6g== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" star-rating-svg "^3.5.0" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/tui-editor@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.1.tgz#bb2bf9540b517e2b7b2a2d250761fbea691c4e4b" - integrity sha512-DjEMl4ZwjzNHSpOzzDjR/XMY0ZnTcn7IOcsJ60q5v1LaMmkqUKQekayyBLFqHNI339BwqSWMyPPB8Q56jpOD3A== +"@abp/tui-editor@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.3.tgz#3039865c149cea98dfedace000c4b392dc0680ed" + integrity sha512-SrwvvLH4GTJg9basCsu1cjmdy7BweDUAmZg0Zy8zZH1ry68FrV7AqGUujopssXkRooP6W/0VRpobbAXEyAAwzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" - "@abp/prismjs" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" + "@abp/prismjs" "~10.5.0-rc.3" -"@abp/uppy@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-10.5.0-rc.1.tgz#c9ccf22939a262f1c9ab56c427f60a6b4da317a9" - integrity sha512-DGDY8JAR23gntOitiUunRzwF+SF6zh6Hav17ImK2LMbdo51Td/oiBPRwSCw2nOx0ovmnE+NrBxnSOCD7doI/ng== +"@abp/uppy@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-10.5.0-rc.3.tgz#5b5a98c7bb59b991be51b96fc3ff63e85484d730" + integrity sha512-LlJRY7PrMCZnFuQLueD++s+HXlwkVJBririh0qU0eODsHmPRmRP+xMk83Ulp9d7Kw5SHUNaqz7j19kyhZs/kDw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" uppy "^5.1.2" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/docs/app/VoloDocs.Web/package.json b/modules/docs/app/VoloDocs.Web/package.json index ab23bef14e..6e47366cbf 100644 --- a/modules/docs/app/VoloDocs.Web/package.json +++ b/modules/docs/app/VoloDocs.Web/package.json @@ -3,7 +3,7 @@ "name": "volo.docstestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/docs": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/docs": "~10.5.0-rc.3" } } diff --git a/modules/docs/app/VoloDocs.Web/yarn.lock b/modules/docs/app/VoloDocs.Web/yarn.lock index 9d7f82a701..bc8cbc238e 100644 --- a/modules/docs/app/VoloDocs.Web/yarn.lock +++ b/modules/docs/app/VoloDocs.Web/yarn.lock @@ -2,222 +2,222 @@ # yarn lockfile v1 -"@abp/anchor-js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/anchor-js/-/anchor-js-10.5.0-rc.1.tgz#d6c78f407961362b597b30fbf4833f9cbf56b351" - integrity sha512-rS8HqE5hemKhALtq52eizjuu7Nuuv9vl0mYknSW/DUru4458SlyjlvPUdTw7SJG8+TbcSMkAq8kXr3AR3AL8YA== +"@abp/anchor-js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/anchor-js/-/anchor-js-10.5.0-rc.3.tgz#39525927b1a6bf36ea3b375275913f169b8347b2" + integrity sha512-r+6LUha2TdoyOp/QTpZhVswPhWvxH9/b0pqe7TlOYNn3nl4mgDCIsF+Ebh4aNxDExK/NL7ZKKqRxm/ri/YLmpA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" anchor-js "^5.0.0" -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.1.tgz#ea71ca4a8bb3f50c75b841b2f5c48fe0d2229023" - integrity sha512-QBd0FWfChBVnRs+0AjLW/PyF+AdrL4IxrjNdJ+xbEzMhJ0nbv5Pjwy4p9+uEB/GSNzaPpE5M+5DR++TgbUEnPw== +"@abp/clipboard@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" + integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/docs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/docs/-/docs-10.5.0-rc.1.tgz#e64bf0bc98fbfc78b944cc7c24791c791d10459f" - integrity sha512-kYZp1mZ3xnCZSi5ZkiNxFoGPjwxCx29fUFQoaS+v2Msg83sqvkb5m9PlepyIc9aXodE4SP1+vAl/1Fu/sAwEeA== +"@abp/docs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/docs/-/docs-10.5.0-rc.3.tgz#d609efdc8dca43965348a524cbc67585de8ef261" + integrity sha512-2meM+okpWB+9gZli+817dPQKb93A88x0GMlTdfKqC0hO9rKNXu6+dadrFXZnVYlhVWLfKGnYOs90rmZZmivnKg== dependencies: - "@abp/anchor-js" "~10.5.0-rc.1" - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" - "@abp/prismjs" "~10.5.0-rc.1" + "@abp/anchor-js" "~10.5.0-rc.3" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" + "@abp/prismjs" "~10.5.0-rc.3" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.1.tgz#fb3e98a918417d5b938db3dc292f52f40b328ddf" - integrity sha512-pbw79D397KBBZabQ9vb3v0HjVr3FMvGo9LqVcxg+nuxTvuu2fPscBhyy6LQGFIMcbz2wGAqS88hGON36AldPRQ== +"@abp/prismjs@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" + integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== dependencies: - "@abp/clipboard" "~10.5.0-rc.1" - "@abp/core" "~10.5.0-rc.1" + "@abp/clipboard" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.3" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestBaseModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestBaseModule.cs index 43bb00cbd9..84a2d22df8 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestBaseModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestBaseModule.cs @@ -1,8 +1,9 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Autofac; -using Volo.Abp.Modularity; -using Volo.Abp.Threading; - +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; +using Volo.Abp.Uow; + namespace Volo.Abp.IdentityServer; [DependsOn( @@ -38,9 +39,17 @@ public class AbpIdentityServerTestBaseModule : AbpModule { using (var scope = context.ServiceProvider.CreateScope()) { - AsyncHelper.RunSync(() => scope.ServiceProvider - .GetRequiredService() - .BuildAsync()); + AsyncHelper.RunSync(async () => + { + var unitOfWorkManager = scope.ServiceProvider.GetRequiredService(); + using (var uow = unitOfWorkManager.Begin(requiresNew: true)) + { + await scope.ServiceProvider + .GetRequiredService() + .BuildAsync(); + await uow.CompleteAsync(); + } + }); } } -} +} diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/package.json b/modules/openiddict/app/OpenIddict.Demo.Server/package.json index 1f101ce7d1..144454257f 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/package.json +++ b/modules/openiddict/app/OpenIddict.Demo.Server/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/modules/openiddict/app/angular/package.json b/modules/openiddict/app/angular/package.json index 4de297a6fa..41d5d4ef39 100644 --- a/modules/openiddict/app/angular/package.json +++ b/modules/openiddict/app/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.1", - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.oauth": "~10.5.0-rc.1", - "@abp/ng.identity": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.tenant-management": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", - "@abp/ng.theme.lepton-x": "~5.5.0-rc.1", + "@abp/ng.account": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.oauth": "~10.5.0-rc.3", + "@abp/ng.identity": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.tenant-management": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", "@angular/animations": "^15.0.1", "@angular/common": "^15.0.1", "@angular/compiler": "^15.0.1", @@ -36,7 +36,7 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.1", + "@abp/ng.schematics": "~10.5.0-rc.3", "@angular-devkit/build-angular": "^15.0.1", "@angular-eslint/builder": "~15.1.0", "@angular-eslint/eslint-plugin": "~15.1.0", diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs index 07be60da44..b076de7cd8 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs @@ -57,24 +57,30 @@ public partial class TokenController if (await externalLoginProvider.TryAuthenticateAsync(request.Username, request.Password)) { - user = await UserManager.FindByNameAsync(request.Username); + user = await UserManager.FindSharedUserByNameAsync(request.Username); if (user == null) { user = await externalLoginProvider.CreateUserAsync(request.Username, externalLoginProviderInfo.Name); } else { - await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); + using (CurrentTenant.Change(user.TenantId)) + { + await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); + } } - return await SetSuccessResultAsync(request, user); + using (CurrentTenant.Change(user.TenantId)) + { + return await SetSuccessResultAsync(request, user); + } } } } await IdentityOptions.SetAsync(); - user = await UserManager.FindByNameAsync(request.Username); + user = await UserManager.FindSharedUserByNameAsync(request.Username); if (user == null) { Logger.LogInformation("No user found matching username: {username}", request.Username); @@ -96,77 +102,82 @@ public partial class TokenController return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } - var result = await SignInManager.CheckPasswordSignInAsync(user, request.Password, true); - if (!result.Succeeded) + using (CurrentTenant.Change(user.TenantId)) { - await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + await IdentityOptions.SetAsync(); + + var result = await SignInManager.CheckPasswordSignInAsync(user, request.Password, true); + if (!result.Succeeded) { - Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, - Action = result.ToIdentitySecurityLogAction(), - UserName = request.Username, - ClientId = request.ClientId - }); + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, + Action = result.ToIdentitySecurityLogAction(), + UserName = request.Username, + ClientId = request.ClientId + }); - var errorCode = OpenIddictConstants.Errors.InvalidGrant; - string errorDescription; + var errorCode = OpenIddictConstants.Errors.InvalidGrant; + string errorDescription; - if (result.IsLockedOut) - { - Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username); - errorCode = AbpOpenIddictErrors.AccountLocked; - errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again."; - } - else if (result.IsNotAllowed) - { - if (!await UserManager.CheckPasswordAsync(user, request.Password)) + if (result.IsLockedOut) { - Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); - errorDescription = "Invalid username or password!"; + Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username); + errorCode = AbpOpenIddictErrors.AccountLocked; + errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again."; } - else + else if (result.IsNotAllowed) { - Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username); - - if (user.ShouldChangePasswordOnNextLogin) + if (!await UserManager.CheckPasswordAsync(user, request.Password)) { - return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password); + Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); + errorDescription = "Invalid username or password!"; } - - if (await UserManager.ShouldPeriodicallyChangePasswordAsync(user)) + else { - return await HandlePeriodicallyChangePasswordAsync(request, user, request.Password); - } + Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username); - if (user.IsActive) - { - return await HandleConfirmUserAsync(request, user); - } + if (user.ShouldChangePasswordOnNextLogin) + { + return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password); + } + + if (await UserManager.ShouldPeriodicallyChangePasswordAsync(user)) + { + return await HandlePeriodicallyChangePasswordAsync(request, user, request.Password); + } - errorCode = AbpOpenIddictErrors.AccountInactive; - errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; + if (user.IsActive) + { + return await HandleConfirmUserAsync(request, user); + } + + errorCode = AbpOpenIddictErrors.AccountInactive; + errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; + } } - } - else - { - Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); - errorDescription = "Invalid username or password!"; + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); + errorDescription = "Invalid username or password!"; + } + + var properties = new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription + }); + + return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } - var properties = new AuthenticationProperties(new Dictionary + if (await IsTfaEnabledAsync(user)) { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription - }); - - return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + return await HandleTwoFactorLoginAsync(request, user); + } - if (await IsTfaEnabledAsync(user)) - { - return await HandleTwoFactorLoginAsync(request, user); + return await SetSuccessResultAsync(request, user); } - - return await SetSuccessResultAsync(request, user); } } } @@ -178,13 +189,13 @@ public partial class TokenController return; } - var userByUsername = await UserManager.FindByNameAsync(request.Username); + var userByUsername = await UserManager.FindSharedUserByNameAsync(request.Username); if (userByUsername != null) { return; } - var userByEmail = await UserManager.FindByEmailAsync(request.Username); + var userByEmail = await UserManager.FindSharedUserByEmailAsync(request.Username); if (userByEmail == null) { return; diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs index 8f17a34be0..81e85057ac 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Security.Claims; +using System.Security.Principal; using System.Text.Json.Nodes; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; @@ -27,66 +28,69 @@ public partial class TokenController // If available, retrieve the claims principal stored in the actor token. var actor = result.Properties?.GetParameter(OpenIddictServerAspNetCoreConstants.Properties.ActorTokenPrincipal); - // Retrieve the user profile corresponding to the subject token. - var user = await UserManager.FindByIdAsync(result.Principal!.GetClaim(OpenIddictConstants.Claims.Subject)!); - if (user is null) + using (CurrentTenant.Change(result.Principal!.FindTenantId())) { - return Forbid( - authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, - properties: new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." - })); - } + // Retrieve the user profile corresponding to the subject token. + var user = await UserManager.FindByIdAsync(result.Principal!.GetClaim(OpenIddictConstants.Claims.Subject)!); + if (user is null) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." + })); + } - // Ensure the user is still allowed to sign in. - if (!await PreSignInCheckAsync(user)) - { - return Forbid( - authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, - properties: new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." - })); - } + // Ensure the user is still allowed to sign in. + if (!await PreSignInCheckAsync(user)) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." + })); + } - // Note: whether the identity represents a delegated or impersonated access (or any other - // model) is entirely up to the implementer: to support all scenarios, OpenIddict doesn't - // enforce any specific constraint on the identity used for the sign-in operation and only - // requires that the standard "act" and "may_act" claims be valid JSON objects if present. + // Note: whether the identity represents a delegated or impersonated access (or any other + // model) is entirely up to the implementer: to support all scenarios, OpenIddict doesn't + // enforce any specific constraint on the identity used for the sign-in operation and only + // requires that the standard "act" and "may_act" claims be valid JSON objects if present. - // Clear the dynamic claims cache. - await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); - // Create a new ClaimsPrincipal containing the claims that - // will be used to create an id_token, a token or a code. - var principal = await SignInManager.CreateUserPrincipalAsync(user); + // Create a new ClaimsPrincipal containing the claims that + // will be used to create an id_token, a token or a code. + var principal = await SignInManager.CreateUserPrincipalAsync(user); - // Note: IdentityModel doesn't support serializing ClaimsIdentity.Actor to the - // standard "act" claim yet, which requires adding the "act" claim manually. - // - // For more information, see - // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3219. - if (!string.IsNullOrEmpty(actor?.GetClaim(OpenIddictConstants.Claims.Subject)) && - !string.Equals(principal.GetClaim(OpenIddictConstants.Claims.Subject), actor.GetClaim(OpenIddictConstants.Claims.Subject), StringComparison.Ordinal)) - { - principal.SetClaim(OpenIddictConstants.Claims.Actor, new JsonObject + // Note: IdentityModel doesn't support serializing ClaimsIdentity.Actor to the + // standard "act" claim yet, which requires adding the "act" claim manually. + // + // For more information, see + // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3219. + if (!string.IsNullOrEmpty(actor?.GetClaim(OpenIddictConstants.Claims.Subject)) && + !string.Equals(principal.GetClaim(OpenIddictConstants.Claims.Subject), actor.GetClaim(OpenIddictConstants.Claims.Subject), StringComparison.Ordinal)) { - [OpenIddictConstants.Claims.Subject] = actor.GetClaim(OpenIddictConstants.Claims.Subject) - }); - } + principal.SetClaim(OpenIddictConstants.Claims.Actor, new JsonObject + { + [OpenIddictConstants.Claims.Subject] = actor.GetClaim(OpenIddictConstants.Claims.Subject) + }); + } - // Note: in this sample, the granted scopes match the requested scope - // but you may want to allow the user to uncheck specific scopes. - // For that, simply restrict the list of scopes before calling SetScopes. - principal.SetScopes(request.GetScopes()); - principal.SetResources(await GetResourcesAsync(request.GetScopes())); + // Note: in this sample, the granted scopes match the requested scope + // but you may want to allow the user to uncheck specific scopes. + // For that, simply restrict the list of scopes before calling SetScopes. + principal.SetScopes(request.GetScopes()); + principal.SetResources(await GetResourcesAsync(request.GetScopes())); - await OpenIddictClaimsPrincipalManager.HandleAsync(request, principal); + await OpenIddictClaimsPrincipalManager.HandleAsync(request, principal); - // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. - return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } } } diff --git a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json index faba9c6ae7..c05c667013 100644 --- a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json +++ b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json @@ -3,6 +3,6 @@ "name": "demo-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock index 6c048b30be..820ecf8a71 100644 --- a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock +++ b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.1.tgz#37c8b67972fe57ab7de8bb71aa136970ff4d391d" - integrity sha512-kwmnxIbYb+TtOWfROGKWekYqDwiA4r0/PoxRH42PpuZk1qsDj9afhLfLv+SWrbXxwq/HYTVgGP/ia7e2Ib+cKw== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" + integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.1.tgz#dac1d97ab5ac508d7b3a648f0f1ad7b853996a85" - integrity sha512-Ey7hEnGYaAIpLdf2hJW5w0oZ1RAsbf7EIDl1HasD80kRJGgT7HFpC/chdzXQ2NYCwOKRk71go4Eh17UfoPH24g== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" + integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.1" - "@abp/bootstrap" "~10.5.0-rc.1" - "@abp/bootstrap-datepicker" "~10.5.0-rc.1" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.1" - "@abp/datatables.net-bs5" "~10.5.0-rc.1" - "@abp/font-awesome" "~10.5.0-rc.1" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.1" - "@abp/lodash" "~10.5.0-rc.1" - "@abp/luxon" "~10.5.0-rc.1" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.1" - "@abp/moment" "~10.5.0-rc.1" - "@abp/select2" "~10.5.0-rc.1" - "@abp/sweetalert2" "~10.5.0-rc.1" - "@abp/timeago" "~10.5.0-rc.1" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.1.tgz#0b02372b49b688d964ceddc3a8b35faf6c5b4872" - integrity sha512-g69xtE710dIvCDtm7oAY6OV1I0+8ajoaLPsoTMf81L7go64Dc7r6N+9aWwPcyV6+PGp/OJOB5f5Bxas4VRuBwA== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" + "@abp/bootstrap" "~10.5.0-rc.3" + "@abp/bootstrap-datepicker" "~10.5.0-rc.3" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" + "@abp/datatables.net-bs5" "~10.5.0-rc.3" + "@abp/font-awesome" "~10.5.0-rc.3" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" + "@abp/lodash" "~10.5.0-rc.3" + "@abp/luxon" "~10.5.0-rc.3" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.3" + "@abp/select2" "~10.5.0-rc.3" + "@abp/sweetalert2" "~10.5.0-rc.3" + "@abp/timeago" "~10.5.0-rc.3" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" + integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.1.tgz#222b56ce30ebfb5da914e8f19c7f229edd9997b7" - integrity sha512-3EjTm1Ao31rnj+kSGm0UgmMlUwRC2khS5tOvSIWD22AOImnU0rkDof9D35pF0odNWWuCs3B5pROwCo+s51C3vA== +"@abp/bootstrap-datepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" + integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.1.tgz#eff0f86e9864a84843132d6c3ba89fb21287545b" - integrity sha512-WPq90hv3dXL8n+Je7HXnl0gv5tcFrELdOWNtjPy/GEXCX5cMFlEY6RhrY8/UN432hupo6Se+S9vfsTD5voGUCg== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" + integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== dependencies: - "@abp/moment" "~10.5.0-rc.1" + "@abp/moment" "~10.5.0-rc.3" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.1.tgz#95cd97a50902b7597d621f3c7086fadf53794988" - integrity sha512-VFkZxIYf1w/oDOXEEYutVgRY/+q5tkToMlt/vrglV0ZAbd5UO8SqWxn68vhDGAjRNwq9AZhep7A2+TKSs5m5Gg== +"@abp/bootstrap@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" + integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== dependencies: - "@abp/core" "~10.5.0-rc.1" - "@abp/popper.js" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" + "@abp/popper.js" "~10.5.0-rc.3" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.1.tgz#5a3a5426f6ebc73d309979a081ef3d69448efad5" - integrity sha512-etMrjlo1YCywb6+WmUte3LZPq+hS8ZWCz/Wxj/d1RHbYiPF6r6kpg88RDFdCZFMMWr8yDOmZ2jR8ZxiWHL37ug== +"@abp/core@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" + integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== dependencies: - "@abp/utils" "~10.5.0-rc.1" + "@abp/utils" "~10.5.0-rc.3" -"@abp/datatables.net-bs5@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.1.tgz#42ed34b6ffb9ac82b2451b4fc9684786d5dc8309" - integrity sha512-JgSXOjOcchR0sknnxbMXc4pqgfTkg5T8I2jTujLFl7dVZqv8DGqRBzmeYljcf3sc7yzvfT6ldBIgiVQWpIEl7Q== +"@abp/datatables.net-bs5@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" + integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== dependencies: - "@abp/datatables.net" "~10.5.0-rc.1" + "@abp/datatables.net" "~10.5.0-rc.3" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.1.tgz#37197a3bb6576276ff51115f365158127cc65a75" - integrity sha512-7ZLpZHouCK6zaiEQaYM1CA51Qwx4eriyu2QH9EPJP9MBfP0IkllU72/If8rVHLtPRF80jaImT9DdV8mf6stNaw== +"@abp/datatables.net@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" + integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.1.tgz#83724be8a8e5153be644b706e6cb9bfad5915da8" - integrity sha512-M06/JGXZHKEFrJcelJDLXH7xdgFsXFH+MGPbXo2GIIHB+lpoiaM/EgHupR/vfhZWwA77RsAQqchDVcYgK7cQtw== +"@abp/font-awesome@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" + integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.1.tgz#864965346f2008b975e5d637880a463f6d402a4f" - integrity sha512-+E3JXG6K0SrO1mKkbHFp1HcxgauP43T583mY7Y64P3tTZi7lwxWGaeEfW4EB6P8jAQJMgSSPBYhXYY1Mg/YxfA== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" + integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.1" + "@abp/jquery-validation" "~10.5.0-rc.3" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.1.tgz#55bd1cec81f6afd3880400cce724a8413259ed84" - integrity sha512-TLIYVHn6QypIlUGmLMYXjCmiW6ZHE6YgAYBn14wlORHn9Uwynt6YtaXKp01V5nbUO8jVxTThJGRaLXidTFE8nA== +"@abp/jquery-validation@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" + integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.1.tgz#8c89638af87ad35b637ccf3db5ed287eaafa825f" - integrity sha512-euL0kUvwpqsMcX71EUwujJdZwYdS5RVZpVAt3INQeNyooS0BzwigkU8xbmvtHle/E8THrbcXgiLJlXLiGXZQBA== +"@abp/jquery@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" + integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.1.tgz#6fea38b6686526c1ebee36b0041eb53dc5a5af86" - integrity sha512-DZY/pY8FCvBz8fyTg7eEnDVZkvrbxqIQAP381qfQJNUa5ONEG86IKkibntV1BI2W9etjb5aVUQ2FlWFUboavtg== +"@abp/lodash@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" + integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.1.tgz#3753dcdf091cddc233d4b8a8056e269852f763d3" - integrity sha512-iezwi4aIsjjPpSspgEDfFJd9Ayibmvl3Hob2oA6hqu2B9GcSjnKVqYgnzuqyl6wRKpYNqE3Hwp4fG0AFEWC0ZA== +"@abp/luxon@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" + integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.1.tgz#163026cecfa92bccc927328b679b59c853bc3f39" - integrity sha512-8HhBsE6sJqwX9sYy3bSbdEnoqBDWQ9NAyx12drr4eMyCLUz66i88DLuMPF9gUxEezNOdIuO9xOWuQ95m3RQH8A== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" + integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.1.tgz#a799d1ca636011aced31090c24f334c54cfd8c32" - integrity sha512-+epGcs5n8+y7hudcFbKa0if8WfqcRRAs6acoqf0XWz006IheIGlJtKxKOTQnFU77QioW1g61ScI/O+1Z7bxvkA== +"@abp/moment@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" + integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.1.tgz#672461b2ec55eb3774be779cc6110dc2fdd44536" - integrity sha512-TXvUWsWJhoeKbhLv9y434+osFTgms1dqzoTYweRe311HyYdL9/iMHPvKigDJSOa00QXljCQj00TsLUVYbhGbLg== +"@abp/popper.js@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" + integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.1.tgz#5945e918ac117db59dbeaa29743f95b8b9f91e8b" - integrity sha512-aEWRADHkdHjMLAiXMpExuf/iq72JpwTnz2iRKP9dadUmyzLTPK+pd4ctKq6ls+NtjGUDbU1DrLbGS3mKNNNSxw== +"@abp/select2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" + integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.1.tgz#89f2c20e1022d222a7b9573768b4a42eef7c5053" - integrity sha512-ZeYAkwk+evzcgPVMaeHfFrqET33LSZ2vhD2cBarpQ8uDrSh5Y+OlhfJGzeM+6b339vm7MCLOHYP64SGoeGJpsQ== +"@abp/sweetalert2@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" + integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== dependencies: - "@abp/core" "~10.5.0-rc.1" + "@abp/core" "~10.5.0-rc.3" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.1.tgz#8a11bad9d51190ac40edc33253033553a7e6e287" - integrity sha512-92Es/rwLHCDaFn3ouMLlLcx+OWGb5LrT0+Ir4XJhu4j4sC/qDHLRV+1pRewL42UGZqt68V+0+3ZRkDa01e3YBA== +"@abp/timeago@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" + integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== dependencies: - "@abp/jquery" "~10.5.0-rc.1" + "@abp/jquery" "~10.5.0-rc.3" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.1": - version "10.5.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.1.tgz#9378bd20940f3e8e6c62a622eff21b88a8d52133" - integrity sha512-dx0QYt4BiD/+4zg38berxTQXAHUex9xtkUvGNlVroHcwoW6l1TTnC1kU9ANI5qONk/dxi/17qnrWD0mrl4357Q== +"@abp/utils@~10.5.0-rc.3": + version "10.5.0-rc.3" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" + integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== dependencies: just-compare "^2.3.0" diff --git a/modules/virtual-file-explorer/app/package.json b/modules/virtual-file-explorer/app/package.json index 3cdb7a044d..56e8558e13 100644 --- a/modules/virtual-file-explorer/app/package.json +++ b/modules/virtual-file-explorer/app/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/virtual-file-explorer": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/virtual-file-explorer": "~10.5.0-rc.3" } } diff --git a/npm/lerna.json b/npm/lerna.json index 6ee0771f34..a7b22d4e2c 100644 --- a/npm/lerna.json +++ b/npm/lerna.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "packages": [ "packs/*" ], diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index bff18f57aa..eee1ae3b7e 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -48,32 +48,31 @@ }, "private": true, "devDependencies": { - "@abp/ng.theme.lepton-x": "~5.5.0-rc.1", - "@abp/ng.schematics": "~10.5.0-rc.1", - "@abp/utils": "~10.5.0-rc.1", - "@angular-devkit/build-angular": "~22.0.0", - "@angular-devkit/core": "~22.0.0", - "@angular-devkit/schematics": "~22.0.0", - "@angular-devkit/schematics-cli": "~22.0.0", - "@angular-eslint/eslint-plugin": "~22.0.0", - "@angular-eslint/eslint-plugin-template": "~22.0.0", - "@angular-eslint/template-parser": "~22.0.0", - "@angular/animations": "~22.0.0", - "@angular/aria": "~22.0.0", - "@angular/build": "~22.0.0", - "@angular/cli": "~22.0.0", - "@angular/common": "~22.0.0", - "@angular/compiler": "~22.0.0", - "@angular/compiler-cli": "~22.0.0", - "@angular/core": "~22.0.0", - "@angular/forms": "~22.0.0", - "@angular/language-service": "~22.0.0", - "@angular/localize": "~22.0.0", - "@angular/platform-browser": "~22.0.0", - "@angular/platform-browser-dynamic": "~22.0.0", - "@angular/platform-server": "~22.0.0", - "@angular/router": "~22.0.0", - "@angular/ssr": "~22.0.0", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", + "@abp/utils": "~10.5.0-rc.3", + "@angular-devkit/build-angular": "~21.2.0", + "@angular-devkit/core": "~21.2.0", + "@angular-devkit/schematics": "~21.2.0", + "@angular-devkit/schematics-cli": "~21.2.0", + "@angular-eslint/eslint-plugin": "~21.2.0", + "@angular-eslint/eslint-plugin-template": "~21.2.0", + "@angular-eslint/template-parser": "~21.2.0", + "@angular/animations": "~21.2.0", + "@angular/aria": "~21.2.0", + "@angular/build": "~21.2.0", + "@angular/cli": "~21.2.0", + "@angular/common": "~21.2.0", + "@angular/compiler": "~21.2.0", + "@angular/compiler-cli": "~21.2.0", + "@angular/core": "~21.2.0", + "@angular/forms": "~21.2.0", + "@angular/language-service": "~21.2.0", + "@angular/localize": "~21.2.0", + "@angular/platform-browser": "~21.2.0", + "@angular/platform-browser-dynamic": "~21.2.0", + "@angular/platform-server": "~21.2.0", + "@angular/router": "~21.2.0", + "@angular/ssr": "~21.2.0", "@fortawesome/fontawesome-free": "^6.0.0", "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngneat/spectator": "~19.6.2", diff --git a/npm/ng-packs/packages/account-core/package.json b/npm/ng-packs/packages/account-core/package.json index 2e4c7938e2..5f0fb64690 100644 --- a/npm/ng-packs/packages/account-core/package.json +++ b/npm/ng-packs/packages/account-core/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.account.core", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index 741602841a..e7f7738a47 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.account", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.core": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.account.core": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/cms-kit/package.json b/npm/ng-packs/packages/cms-kit/package.json index 6d26a586d0..861d7d133b 100644 --- a/npm/ng-packs/packages/cms-kit/package.json +++ b/npm/ng-packs/packages/cms-kit/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.cms-kit", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "@toast-ui/editor": "~3.0.0", "codemirror": "~6.0.0", "tslib": "^2.0.0" diff --git a/npm/ng-packs/packages/components/package.json b/npm/ng-packs/packages/components/package.json index ba34aa21d2..1419c747bd 100644 --- a/npm/ng-packs/packages/components/package.json +++ b/npm/ng-packs/packages/components/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.components", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "peerDependencies": { - "@abp/ng.core": ">=10.5.0-rc.1", - "@abp/ng.theme.shared": ">=10.5.0-rc.1" + "@abp/ng.core": ">=10.5.0-rc.3", + "@abp/ng.theme.shared": ">=10.5.0-rc.3" }, "dependencies": { "chart.js": "^3.5.1", diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index 23d4830b09..249b5d4170 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.core", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/utils": "~10.5.0-rc.1", + "@abp/utils": "~10.5.0-rc.3", "just-clone": "^6.0.0", "just-compare": "^2.0.0", "ts-toolbelt": "^9.0.0", diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index 2af7c126de..dc60396b66 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.feature-management", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/generators/package.json b/npm/ng-packs/packages/generators/package.json index 5825fd23e8..ca377f820a 100644 --- a/npm/ng-packs/packages/generators/package.json +++ b/npm/ng-packs/packages/generators/package.json @@ -1,6 +1,6 @@ { "name": "@abp/nx.generators", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "generators": "./generators.json", "type": "commonjs", diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 6989fcbedb..de8fefc31c 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.identity", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.permission-management": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.permission-management": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json index f89b1c20f9..04a7ff826e 100644 --- a/npm/ng-packs/packages/oauth/package.json +++ b/npm/ng-packs/packages/oauth/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.oauth", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/utils": "~10.5.0-rc.1", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/utils": "~10.5.0-rc.3", "angular-oauth2-oidc": "^20.0.0", "just-clone": "^6.0.0", "just-compare": "^2.0.0", diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index 096f032f6c..85edab5b82 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.permission-management", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index e8d1a3dbcb..9250064323 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.schematics", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "author": "", "schematics": "./collection.json", "dependencies": { diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 2b1791e5ad..9166b38a9b 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.setting-management", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 66159b1420..4e1b06c290 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.tenant-management", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.feature-management": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.feature-management": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index 2f7bab606d..5eac6adb43 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.theme.basic", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.core": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.account.core": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 4d5627b8e5..9cf2894626 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.theme.shared", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~10.5.0-rc.1", + "@abp/ng.core": "~10.5.0-rc.3", "@fortawesome/fontawesome-free": "^6.0.0", "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngx-validate/core": "^0.2.0", diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts index 2d1fbb0ddc..3c5c01c9f9 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts @@ -47,32 +47,31 @@ export class LoadingDirective implements OnInit, OnDestroy { private handleLoadingChange(newValue: boolean) { setTimeout(() => { - if (!newValue && this.timerSubscription) { - this.timerSubscription.unsubscribe(); - this.timerSubscription = null; - - if (this.rootNode) { - this.renderer.removeChild(this.rootNode.parentElement, this.rootNode); - this.rootNode = null; - } + if (!newValue) { + this.clearLoading(); return; } + if (this.timerSubscription) { + this.timerSubscription.unsubscribe(); + } + this.timerSubscription = timer(this.delay()) .pipe(take(1)) .subscribe(() => { + if (!this.loading()) { + return; + } + if (!this.componentRef) { this.componentRef = this.viewContainerRef.createComponent(LoadingComponent, { injector: this.injector }); } - if (newValue && !this.rootNode) { + if (!this.rootNode) { this.rootNode = (this.componentRef.hostView as EmbeddedViewRef).rootNodes[0]; this.targetElement?.appendChild(this.rootNode as HTMLDivElement); - } else if (this.rootNode) { - this.renderer.removeChild(this.rootNode.parentElement, this.rootNode); - this.rootNode = null; } this.timerSubscription = null; @@ -80,6 +79,18 @@ export class LoadingDirective implements OnInit, OnDestroy { }, 0); } + private clearLoading() { + if (this.timerSubscription) { + this.timerSubscription.unsubscribe(); + this.timerSubscription = null; + } + + if (this.rootNode?.parentElement) { + this.renderer.removeChild(this.rootNode.parentElement, this.rootNode); + this.rootNode = null; + } + } + ngOnInit() { this.targetElement = this.targetElementInput(); if (!this.targetElement) { @@ -93,8 +104,6 @@ export class LoadingDirective implements OnInit, OnDestroy { } ngOnDestroy() { - if (this.timerSubscription) { - this.timerSubscription.unsubscribe(); - } + this.clearLoading(); } } diff --git a/npm/packs/anchor-js/package.json b/npm/packs/anchor-js/package.json index 37fcd3059b..14c41d93eb 100644 --- a/npm/packs/anchor-js/package.json +++ b/npm/packs/anchor-js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/anchor-js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "anchor-js": "^5.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/aspnetcore.components.server.basictheme/package.json b/npm/packs/aspnetcore.components.server.basictheme/package.json index ef845d4c22..00637d758b 100644 --- a/npm/packs/aspnetcore.components.server.basictheme/package.json +++ b/npm/packs/aspnetcore.components.server.basictheme/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/aspnetcore.components.server.basictheme", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.components.server.theming": "~10.5.0-rc.1" + "@abp/aspnetcore.components.server.theming": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.components.server.theming/package.json b/npm/packs/aspnetcore.components.server.theming/package.json index cd414ea3c2..cbf24167df 100644 --- a/npm/packs/aspnetcore.components.server.theming/package.json +++ b/npm/packs/aspnetcore.components.server.theming/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/aspnetcore.components.server.theming", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/bootstrap": "~10.5.0-rc.1", - "@abp/font-awesome": "~10.5.0-rc.1" + "@abp/bootstrap": "~10.5.0-rc.3", + "@abp/font-awesome": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json index 2eb5c76bfc..ee5021df43 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/aspnetcore.mvc.ui.theme.basic", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json index 9c45bf569f..3b11314d4e 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/aspnetcore.mvc.ui.theme.shared", "repository": { "type": "git", @@ -10,20 +10,20 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui": "~10.5.0-rc.1", - "@abp/bootstrap": "~10.5.0-rc.1", - "@abp/bootstrap-datepicker": "~10.5.0-rc.1", - "@abp/bootstrap-daterangepicker": "~10.5.0-rc.1", - "@abp/datatables.net-bs5": "~10.5.0-rc.1", - "@abp/font-awesome": "~10.5.0-rc.1", - "@abp/jquery-validation-unobtrusive": "~10.5.0-rc.1", - "@abp/lodash": "~10.5.0-rc.1", - "@abp/luxon": "~10.5.0-rc.1", - "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.1", - "@abp/moment": "~10.5.0-rc.1", - "@abp/select2": "~10.5.0-rc.1", - "@abp/sweetalert2": "~10.5.0-rc.1", - "@abp/timeago": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui": "~10.5.0-rc.3", + "@abp/bootstrap": "~10.5.0-rc.3", + "@abp/bootstrap-datepicker": "~10.5.0-rc.3", + "@abp/bootstrap-daterangepicker": "~10.5.0-rc.3", + "@abp/datatables.net-bs5": "~10.5.0-rc.3", + "@abp/font-awesome": "~10.5.0-rc.3", + "@abp/jquery-validation-unobtrusive": "~10.5.0-rc.3", + "@abp/lodash": "~10.5.0-rc.3", + "@abp/luxon": "~10.5.0-rc.3", + "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.3", + "@abp/moment": "~10.5.0-rc.3", + "@abp/select2": "~10.5.0-rc.3", + "@abp/sweetalert2": "~10.5.0-rc.3", + "@abp/timeago": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui/package-lock.json b/npm/packs/aspnetcore.mvc.ui/package-lock.json index 11ae522cb7..d4e696b179 100644 --- a/npm/packs/aspnetcore.mvc.ui/package-lock.json +++ b/npm/packs/aspnetcore.mvc.ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "@abp/aspnetcore.mvc.ui", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "lockfileVersion": 1, "requires": true, "packages": { diff --git a/npm/packs/aspnetcore.mvc.ui/package.json b/npm/packs/aspnetcore.mvc.ui/package.json index 4b318e0a37..359428439f 100644 --- a/npm/packs/aspnetcore.mvc.ui/package.json +++ b/npm/packs/aspnetcore.mvc.ui/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/aspnetcore.mvc.ui", "repository": { "type": "git", diff --git a/npm/packs/blogging/package.json b/npm/packs/blogging/package.json index 78389ab022..c68e6ffe22 100644 --- a/npm/packs/blogging/package.json +++ b/npm/packs/blogging/package.json @@ -1,14 +1,14 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/blogging", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.1", - "@abp/owl.carousel": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1", - "@abp/tui-editor": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3", + "@abp/owl.carousel": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3", + "@abp/tui-editor": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/bootstrap-datepicker/package.json b/npm/packs/bootstrap-datepicker/package.json index aa016fa59d..ff12c51cfe 100644 --- a/npm/packs/bootstrap-datepicker/package.json +++ b/npm/packs/bootstrap-datepicker/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/bootstrap-datepicker", "repository": { "type": "git", diff --git a/npm/packs/bootstrap-daterangepicker/package.json b/npm/packs/bootstrap-daterangepicker/package.json index 4c83fec89d..2757b14687 100644 --- a/npm/packs/bootstrap-daterangepicker/package.json +++ b/npm/packs/bootstrap-daterangepicker/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/bootstrap-daterangepicker", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/moment": "~10.5.0-rc.1", + "@abp/moment": "~10.5.0-rc.3", "bootstrap-daterangepicker": "^3.1.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/bootstrap/package.json b/npm/packs/bootstrap/package.json index e39b565578..3fa5cb0400 100644 --- a/npm/packs/bootstrap/package.json +++ b/npm/packs/bootstrap/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/bootstrap", "repository": { "type": "git", @@ -10,8 +10,8 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", - "@abp/popper.js": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", + "@abp/popper.js": "~10.5.0-rc.3", "bootstrap": "^5.3.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/chart.js/package.json b/npm/packs/chart.js/package.json index ad679fa2be..ee66b1be44 100644 --- a/npm/packs/chart.js/package.json +++ b/npm/packs/chart.js/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/chart.js", "publishConfig": { "access": "public" diff --git a/npm/packs/clipboard/package.json b/npm/packs/clipboard/package.json index d1632a0cb5..5b6993cd53 100644 --- a/npm/packs/clipboard/package.json +++ b/npm/packs/clipboard/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/clipboard", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "clipboard": "^2.0.11" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/cms-kit.admin/package.json b/npm/packs/cms-kit.admin/package.json index ed940a67f8..7d57b03bfc 100644 --- a/npm/packs/cms-kit.admin/package.json +++ b/npm/packs/cms-kit.admin/package.json @@ -1,16 +1,16 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/cms-kit.admin", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/codemirror": "~10.5.0-rc.1", - "@abp/jstree": "~10.5.0-rc.1", - "@abp/markdown-it": "~10.5.0-rc.1", - "@abp/slugify": "~10.5.0-rc.1", - "@abp/tui-editor": "~10.5.0-rc.1", - "@abp/uppy": "~10.5.0-rc.1" + "@abp/codemirror": "~10.5.0-rc.3", + "@abp/jstree": "~10.5.0-rc.3", + "@abp/markdown-it": "~10.5.0-rc.3", + "@abp/slugify": "~10.5.0-rc.3", + "@abp/tui-editor": "~10.5.0-rc.3", + "@abp/uppy": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit.public/package.json b/npm/packs/cms-kit.public/package.json index 8686a4eddc..a4ed9b143a 100644 --- a/npm/packs/cms-kit.public/package.json +++ b/npm/packs/cms-kit.public/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/cms-kit.public", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/highlight.js": "~10.5.0-rc.1", - "@abp/star-rating-svg": "~10.5.0-rc.1" + "@abp/highlight.js": "~10.5.0-rc.3", + "@abp/star-rating-svg": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit/package.json b/npm/packs/cms-kit/package.json index bbbbacb60a..7807ecfb76 100644 --- a/npm/packs/cms-kit/package.json +++ b/npm/packs/cms-kit/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/cms-kit", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/cms-kit.admin": "~10.5.0-rc.1", - "@abp/cms-kit.public": "~10.5.0-rc.1" + "@abp/cms-kit.admin": "~10.5.0-rc.3", + "@abp/cms-kit.public": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/codemirror/package.json b/npm/packs/codemirror/package.json index ede359e8ed..90dbcf8193 100644 --- a/npm/packs/codemirror/package.json +++ b/npm/packs/codemirror/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/codemirror", "publishConfig": { "access": "public" @@ -8,7 +8,7 @@ "build": "node ./scripts/build.js" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "@codemirror/lang-css": "^6.0.0", "@codemirror/lang-javascript": "^6.0.0", "@codemirror/state": "^6.0.0", diff --git a/npm/packs/core/package.json b/npm/packs/core/package.json index e3e6e86d5c..78728f1f4e 100644 --- a/npm/packs/core/package.json +++ b/npm/packs/core/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/core", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/utils": "~10.5.0-rc.1" + "@abp/utils": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cropperjs/package.json b/npm/packs/cropperjs/package.json index 9f3fbbbf9c..42ca25e3c7 100644 --- a/npm/packs/cropperjs/package.json +++ b/npm/packs/cropperjs/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/cropperjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "cropperjs": "^1.6.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs4/package.json b/npm/packs/datatables.net-bs4/package.json index 852e30816a..d887f587ca 100644 --- a/npm/packs/datatables.net-bs4/package.json +++ b/npm/packs/datatables.net-bs4/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/datatables.net-bs4", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/datatables.net": "~10.5.0-rc.1", + "@abp/datatables.net": "~10.5.0-rc.3", "datatables.net-bs4": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs5/package.json b/npm/packs/datatables.net-bs5/package.json index e051c34e4f..ce6cd4ef69 100644 --- a/npm/packs/datatables.net-bs5/package.json +++ b/npm/packs/datatables.net-bs5/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/datatables.net-bs5", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/datatables.net": "~10.5.0-rc.1", + "@abp/datatables.net": "~10.5.0-rc.3", "datatables.net-bs5": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net/package.json b/npm/packs/datatables.net/package.json index 25d49ab41f..ada4be0e82 100644 --- a/npm/packs/datatables.net/package.json +++ b/npm/packs/datatables.net/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/datatables.net", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "datatables.net": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/docs/package.json b/npm/packs/docs/package.json index ea84710f86..009e459c1b 100644 --- a/npm/packs/docs/package.json +++ b/npm/packs/docs/package.json @@ -1,15 +1,15 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/docs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/anchor-js": "~10.5.0-rc.1", - "@abp/clipboard": "~10.5.0-rc.1", - "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.1", - "@abp/popper.js": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1" + "@abp/anchor-js": "~10.5.0-rc.3", + "@abp/clipboard": "~10.5.0-rc.3", + "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.3", + "@abp/popper.js": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/flag-icon-css/package.json b/npm/packs/flag-icon-css/package.json index 53a01f315e..26553ba07a 100644 --- a/npm/packs/flag-icon-css/package.json +++ b/npm/packs/flag-icon-css/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/flag-icon-css", "publishConfig": { "access": "public" diff --git a/npm/packs/flag-icons/package.json b/npm/packs/flag-icons/package.json index f999c77d0f..ca33fe8cc9 100644 --- a/npm/packs/flag-icons/package.json +++ b/npm/packs/flag-icons/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/flag-icons", "publishConfig": { "access": "public" diff --git a/npm/packs/font-awesome/package.json b/npm/packs/font-awesome/package.json index 780d3a12eb..f86a333be5 100644 --- a/npm/packs/font-awesome/package.json +++ b/npm/packs/font-awesome/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/font-awesome", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "@fortawesome/fontawesome-free": "^7.0.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/highlight.js/package.json b/npm/packs/highlight.js/package.json index b24a285c23..226a5764c2 100644 --- a/npm/packs/highlight.js/package.json +++ b/npm/packs/highlight.js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/highlight.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "@highlightjs/cdn-assets": "~11.11.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-form/package.json b/npm/packs/jquery-form/package.json index adb598f49f..34057c9667 100644 --- a/npm/packs/jquery-form/package.json +++ b/npm/packs/jquery-form/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/jquery-form", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "jquery-form": "^4.3.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation-unobtrusive/package.json b/npm/packs/jquery-validation-unobtrusive/package.json index 1d1824bf39..c6ab79e4fd 100644 --- a/npm/packs/jquery-validation-unobtrusive/package.json +++ b/npm/packs/jquery-validation-unobtrusive/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/jquery-validation-unobtrusive", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery-validation": "~10.5.0-rc.1", + "@abp/jquery-validation": "~10.5.0-rc.3", "jquery-validation-unobtrusive": "^4.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation/package.json b/npm/packs/jquery-validation/package.json index 925a10319c..ae7e938b24 100644 --- a/npm/packs/jquery-validation/package.json +++ b/npm/packs/jquery-validation/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/jquery-validation", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "jquery-validation": "^1.21.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery/package.json b/npm/packs/jquery/package.json index edd74159e1..0cc8c01c75 100644 --- a/npm/packs/jquery/package.json +++ b/npm/packs/jquery/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/jquery", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "jquery": "~3.7.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jstree/package.json b/npm/packs/jstree/package.json index e8f32c82fa..66fb8c5a1f 100644 --- a/npm/packs/jstree/package.json +++ b/npm/packs/jstree/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/jstree", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "jstree": "^3.3.17" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/lodash/package.json b/npm/packs/lodash/package.json index 9b967150ae..d1a565c618 100644 --- a/npm/packs/lodash/package.json +++ b/npm/packs/lodash/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/lodash", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "lodash": "^4.18.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/luxon/package.json b/npm/packs/luxon/package.json index 29f0f2bba1..383c86c109 100644 --- a/npm/packs/luxon/package.json +++ b/npm/packs/luxon/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/luxon", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "luxon": "^3.7.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/malihu-custom-scrollbar-plugin/package.json b/npm/packs/malihu-custom-scrollbar-plugin/package.json index 1ec891410a..311750c55b 100644 --- a/npm/packs/malihu-custom-scrollbar-plugin/package.json +++ b/npm/packs/malihu-custom-scrollbar-plugin/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/malihu-custom-scrollbar-plugin", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "malihu-custom-scrollbar-plugin": "^3.1.5" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/markdown-it/package.json b/npm/packs/markdown-it/package.json index f803012593..1b5c8afe54 100644 --- a/npm/packs/markdown-it/package.json +++ b/npm/packs/markdown-it/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/markdown-it", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "markdown-it": "^14.1.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/moment/package.json b/npm/packs/moment/package.json index cf17841fe2..05186f6af0 100644 --- a/npm/packs/moment/package.json +++ b/npm/packs/moment/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/moment", "repository": { "type": "git", diff --git a/npm/packs/owl.carousel/package.json b/npm/packs/owl.carousel/package.json index 6abd686342..b1124b4fe4 100644 --- a/npm/packs/owl.carousel/package.json +++ b/npm/packs/owl.carousel/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/owl.carousel", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "owl.carousel": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/popper.js/package.json b/npm/packs/popper.js/package.json index 8461e10a50..d45a46d17a 100644 --- a/npm/packs/popper.js/package.json +++ b/npm/packs/popper.js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/popper.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "@popperjs/core": "^2.11.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/prismjs/package.json b/npm/packs/prismjs/package.json index 8e946ac43a..04c041763b 100644 --- a/npm/packs/prismjs/package.json +++ b/npm/packs/prismjs/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/prismjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~10.5.0-rc.1", - "@abp/core": "~10.5.0-rc.1", + "@abp/clipboard": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.3", "prismjs": "^1.30.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/qrcode/package.json b/npm/packs/qrcode/package.json index 573631da8c..c4157c6a7e 100644 --- a/npm/packs/qrcode/package.json +++ b/npm/packs/qrcode/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/qrcode", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1" + "@abp/core": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/select2/package.json b/npm/packs/select2/package.json index 9b4da7984a..5b4b979289 100644 --- a/npm/packs/select2/package.json +++ b/npm/packs/select2/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/select2", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "select2": "^4.0.13" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/signalr/package.json b/npm/packs/signalr/package.json index 8d23247944..e932121445 100644 --- a/npm/packs/signalr/package.json +++ b/npm/packs/signalr/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/signalr", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "@microsoft/signalr": "~9.0.6" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/slugify/package.json b/npm/packs/slugify/package.json index 1693c63500..981d48547d 100644 --- a/npm/packs/slugify/package.json +++ b/npm/packs/slugify/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/slugify", "publishConfig": { "access": "public" diff --git a/npm/packs/star-rating-svg/package.json b/npm/packs/star-rating-svg/package.json index 99394fbd62..5dc84acd3f 100644 --- a/npm/packs/star-rating-svg/package.json +++ b/npm/packs/star-rating-svg/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/star-rating-svg", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "star-rating-svg": "^3.5.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/sweetalert2/package.json b/npm/packs/sweetalert2/package.json index c8bd40d854..f7d736b4f2 100644 --- a/npm/packs/sweetalert2/package.json +++ b/npm/packs/sweetalert2/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/sweetalert2", "publishConfig": { "access": "public" @@ -10,7 +10,7 @@ "directory": "npm/packs/sweetalert2" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "sweetalert2": "^11.23.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/timeago/package.json b/npm/packs/timeago/package.json index 400330f106..45d3b438f7 100644 --- a/npm/packs/timeago/package.json +++ b/npm/packs/timeago/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/timeago", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "timeago": "^1.6.7" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/toastr/package.json b/npm/packs/toastr/package.json index fd5b7e35f1..7607765044 100644 --- a/npm/packs/toastr/package.json +++ b/npm/packs/toastr/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/toastr", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", + "@abp/jquery": "~10.5.0-rc.3", "toastr": "^2.1.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/tui-editor/package.json b/npm/packs/tui-editor/package.json index f5c260f23c..98bd47dacc 100644 --- a/npm/packs/tui-editor/package.json +++ b/npm/packs/tui-editor/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/tui-editor", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1" + "@abp/jquery": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/uppy/package.json b/npm/packs/uppy/package.json index 10372c69cb..9cd28abe98 100644 --- a/npm/packs/uppy/package.json +++ b/npm/packs/uppy/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/uppy", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "uppy": "^5.1.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/utils/package.json b/npm/packs/utils/package.json index 7115183b39..8d691ee210 100644 --- a/npm/packs/utils/package.json +++ b/npm/packs/utils/package.json @@ -1,6 +1,6 @@ { "name": "@abp/utils", - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "scripts": { "prepublishOnly": "yarn install --ignore-scripts && node prepublish.js", "ng": "ng", diff --git a/npm/packs/vee-validate/package.json b/npm/packs/vee-validate/package.json index 062069c37f..097a23fef3 100644 --- a/npm/packs/vee-validate/package.json +++ b/npm/packs/vee-validate/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/vee-validate", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/vue": "~10.5.0-rc.1", + "@abp/vue": "~10.5.0-rc.3", "vee-validate": "~3.4.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/virtual-file-explorer/package.json b/npm/packs/virtual-file-explorer/package.json index 38468bf1fa..c2425a5d46 100644 --- a/npm/packs/virtual-file-explorer/package.json +++ b/npm/packs/virtual-file-explorer/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/virtual-file-explorer", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~10.5.0-rc.1", - "@abp/prismjs": "~10.5.0-rc.1" + "@abp/clipboard": "~10.5.0-rc.3", + "@abp/prismjs": "~10.5.0-rc.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/vue/package.json b/npm/packs/vue/package.json index 38a56d76ab..b9c4328665 100644 --- a/npm/packs/vue/package.json +++ b/npm/packs/vue/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/vue", "publishConfig": { "access": "public" diff --git a/npm/packs/zxcvbn/package.json b/npm/packs/zxcvbn/package.json index 47affda998..457cb6e177 100644 --- a/npm/packs/zxcvbn/package.json +++ b/npm/packs/zxcvbn/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.1", + "version": "10.5.0-rc.3", "name": "@abp/zxcvbn", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.1", + "@abp/core": "~10.5.0-rc.3", "zxcvbn": "^4.4.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/schemas/low-code/definitions/command-interceptor-descriptor.schema.json b/schemas/low-code/definitions/command-interceptor-descriptor.schema.json new file mode 100644 index 0000000000..ee1fc6d49a --- /dev/null +++ b/schemas/low-code/definitions/command-interceptor-descriptor.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "command-interceptor-descriptor.schema.json", + "title": "CommandInterceptorDescriptor", + "description": "Describes a JavaScript interceptor for an entity Create, Update, or Delete command.", + "markdownDescription": "AI guidance: use `type: \"Pre\"` to validate or block before persistence and `type: \"Post\"` for side effects after persistence. Scripts can inspect `context.commandArgs` and may use services exposed on `context` such as `db`, `currentUser`, `currentTenant`, `emailSender`, `config`, `http`, and logging helpers depending on host configuration. To block the command with a user-facing error, assign `globalError = \"message\"` and return. Keep scripts idempotent where possible.", + "type": "object", + "properties": { + "commandName": { + "type": "string", + "description": "Entity command to intercept: Create, Update, or Delete.", + "enum": ["Create", "Update", "Delete"] + }, + "type": { + "$ref": "interceptor-type.schema.json", + "description": "Whether the script runs before or after the command." + }, + "javascript": { + "type": "string", + "description": "JavaScript code to execute. For Pre interceptors, set globalError to block the command. Example: if (!context.commandArgs.data['Name']) { globalError = 'Name is required.'; }" + } + }, + "required": ["commandName", "type", "javascript"], + "additionalProperties": false, + "examples": [ + { + "commandName": "Create", + "type": "Pre", + "javascript": "if (!context.commandArgs.data['Name']) { globalError = 'Name is required.'; }" + }, + { + "commandName": "Delete", + "type": "Pre", + "javascript": "var record = await db.get('Acme.Events.Event', context.commandArgs.entityId); if (record && record.Status === 2) { globalError = 'Completed events cannot be deleted.'; }" + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-aggregation-type.schema.json b/schemas/low-code/definitions/dashboard-aggregation-type.schema.json new file mode 100644 index 0000000000..0de9d6da48 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-aggregation-type.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-aggregation-type.schema.json", + "title": "DashboardAggregationType", + "description": "The type of aggregation to perform. Prefer lower-case values in descriptor JSON; PascalCase aliases are accepted for compatibility.", + "markdownDescription": "`count` counts records and does not require a property. `sum`, `average`, `min`, and `max` require a property. `percentFilled` and `percentEmpty` calculate completeness for a nullable/string property.", + "type": "string", + "enum": [ + "count", + "Count", + "sum", + "Sum", + "average", + "Average", + "min", + "Min", + "max", + "Max", + "percentFilled", + "PercentFilled", + "percentEmpty", + "PercentEmpty" + ], + "enumDescriptions": [ + "Count matching records.", + "Count matching records.", + "Sum a numeric property.", + "Sum a numeric property.", + "Average a numeric property.", + "Average a numeric property.", + "Minimum property value.", + "Minimum property value.", + "Maximum property value.", + "Maximum property value.", + "Percentage of records where property has a value.", + "Percentage of records where property has a value.", + "Percentage of records where property is empty/null.", + "Percentage of records where property is empty/null." + ] +} diff --git a/schemas/low-code/definitions/dashboard-chart-descriptor.schema.json b/schemas/low-code/definitions/dashboard-chart-descriptor.schema.json new file mode 100644 index 0000000000..ade648db0f --- /dev/null +++ b/schemas/low-code/definitions/dashboard-chart-descriptor.schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-chart-descriptor.schema.json", + "title": "DashboardChartDescriptor", + "description": "Configuration for a chart visualization.", + "markdownDescription": "AI guidance: use chart visualizations for grouped aggregations. `xAxis.property` is the grouping property. `yAxis` contains one or more aggregations. Use `count` without a property; use `sum`, `average`, `min`, or `max` with a numeric/date property. Use `dateGrouping` when xAxis is date/datetime.", + "type": "object", + "properties": { + "chartType": { + "$ref": "dashboard-chart-type.schema.json", + "description": "Chart renderer type: bar, line, pie, or donut." + }, + "xAxis": { + "type": "object", + "properties": { + "property": { + "type": "string", + "description": "Property name to group by on the X-axis. Must exist on the visualization entity." + }, + "useForeignDisplay": { + "type": "boolean", + "description": "Show the FK display property instead of the raw id when xAxis.property is a foreign key.", + "default": false + }, + "dateGrouping": { + "type": ["string", "null"], + "enum": ["", "day", "week", "month", "quarter", "year", null], + "description": "Grouping interval for date/datetime properties. Omit or use empty string for no date grouping." + } + }, + "required": ["property"], + "additionalProperties": false + }, + "yAxis": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aggregation": { + "$ref": "dashboard-aggregation-type.schema.json" + }, + "property": { + "type": ["string", "null"], + "description": "Property name for sum/average/min/max/percent aggregations. Omit for count." + }, + "label": { + "type": ["string", "null"], + "description": "Display label for this series. Omit to derive from aggregation/property." + }, + "color": { + "type": ["string", "null"] + } + }, + "required": ["aggregation"], + "additionalProperties": false + }, + "minItems": 1 + }, + "barOrientation": { + "type": "string", + "enum": ["vertical", "horizontal"], + "default": "vertical" + }, + "showRecordCount": { + "type": "boolean", + "default": false + }, + "maxItems": { + "type": "integer", + "description": "Maximum number of grouped items to display in the chart.", + "default": 10, + "minimum": 1, + "maximum": 50 + } + }, + "required": ["chartType", "xAxis", "yAxis"], + "additionalProperties": false, + "examples": [ + { + "chartType": "bar", + "xAxis": { "property": "Status" }, + "yAxis": [{ "aggregation": "count", "label": "Events" }], + "maxItems": 10 + }, + { + "chartType": "line", + "xAxis": { "property": "StartDate", "dateGrouping": "month" }, + "yAxis": [{ "aggregation": "sum", "property": "Budget", "label": "Budget" }] + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-chart-type.schema.json b/schemas/low-code/definitions/dashboard-chart-type.schema.json new file mode 100644 index 0000000000..fe3b2124e5 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-chart-type.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-chart-type.schema.json", + "title": "DashboardChartType", + "description": "The type of chart. Prefer lower-case values in descriptor JSON; PascalCase aliases are accepted for compatibility.", + "markdownDescription": "Use `bar` for categorical comparisons, `line` for trends over time, `pie`/`donut` for part-of-whole views with a small number of categories.", + "type": "string", + "enum": ["bar", "Bar", "line", "Line", "pie", "Pie", "donut", "Donut"], + "enumDescriptions": [ + "Bar chart.", + "Bar chart.", + "Line chart.", + "Line chart.", + "Pie chart.", + "Pie chart.", + "Donut chart.", + "Donut chart." + ] +} diff --git a/schemas/low-code/definitions/dashboard-descriptor.schema.json b/schemas/low-code/definitions/dashboard-descriptor.schema.json new file mode 100644 index 0000000000..c0c77d7be9 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-descriptor.schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-descriptor.schema.json", + "title": "DashboardDescriptor", + "description": "Describes a dashboard page configuration with global filters and a flat list of visualizations.", + "markdownDescription": "AI guidance: a dashboard belongs to a page with `type: \"dashboard\"`. It contains a flat `visualizations` list; each visualization carries its grid position via `row`, `order`, and `width`. Rows are derived at render time by grouping visualizations that share the same zero-based `row`. Use visualization `entityName` to select data for chart/list items. Use numberContainer for KPI tiles, charts for grouped aggregations, and lists for recent/top records.", + "type": "object", + "properties": { + "description": { + "type": ["string", "null"], + "description": "Optional description text shown below the dashboard title." + }, + "globalFilters": { + "type": "array", + "description": "Global filters that affect visualizations, for example a date range. Visualizations can map the global date filter through globalDateFilterProperty.", + "items": { + "$ref": "dashboard-global-filter-descriptor.schema.json" + } + }, + "visualizations": { + "type": "array", + "description": "Flat list of name-identified dashboard visualizations. Each visualization positions itself on the grid via row, order, and width.", + "items": { + "$ref": "dashboard-visualization-descriptor.schema.json" + }, + "minItems": 1 + } + }, + "required": ["visualizations"], + "additionalProperties": false, + "examples": [ + { + "description": "Operational overview", + "visualizations": [ + { + "name": "events-by-status", + "type": "chart", + "title": "Events by Status", + "row": 0, + "order": 0, + "width": 2, + "entityName": "Acme.Events.Event", + "chart": { + "chartType": "bar", + "xAxis": { "property": "Status" }, + "yAxis": [{ "aggregation": "count", "label": "Events" }] + } + } + ] + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-filter-descriptor.schema.json b/schemas/low-code/definitions/dashboard-filter-descriptor.schema.json new file mode 100644 index 0000000000..93cb76e821 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-filter-descriptor.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-filter-descriptor.schema.json", + "title": "DashboardFilterDescriptor", + "description": "Static filter applied to a dashboard visualization or KPI item.", + "markdownDescription": "AI guidance: use dashboard filters to constrain the data behind a visualization, for example only active records or only records above a threshold. Each condition property must exist on the visualization/item entity. Combine conditions with `operator: \"and\"` or `operator: \"or\"`.", + "type": "object", + "properties": { + "operator": { + "type": "string", + "enum": ["and", "or"], + "default": "and" + }, + "conditions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "property": { + "type": "string", + "description": "Property name to filter on. Must exist on the visualization/item entityName." + }, + "filterType": { + "type": "string", + "enum": ["equal", "notEqual", "contains", "greaterThan", "lessThan", "isNull", "isNotNull"], + "default": "equal" + }, + "value": { + "description": "Filter value. Shape depends on filterType and target property type." + } + }, + "required": ["property", "filterType"], + "additionalProperties": false + } + } + }, + "required": ["conditions"], + "additionalProperties": false, + "examples": [ + { + "operator": "and", + "conditions": [ + { "property": "Status", "filterType": "equal", "value": 1 } + ] + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-global-filter-descriptor.schema.json b/schemas/low-code/definitions/dashboard-global-filter-descriptor.schema.json new file mode 100644 index 0000000000..ab1d3f79a6 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-global-filter-descriptor.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-global-filter-descriptor.schema.json", + "title": "DashboardGlobalFilterDescriptor", + "description": "Describes a dashboard-level filter control that can affect all visualizations on the dashboard.", + "markdownDescription": "AI guidance: use global filters for dashboard-wide controls, not for per-visualization conditions. The current supported type is `dateRange`; visualization filters can then reference date-like entity properties through their own `filter` or `userFilters` settings.", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["dateRange"], + "default": "dateRange", + "description": "Global filter control type. Currently only 'dateRange' is supported." + } + }, + "required": ["type"], + "additionalProperties": false, + "examples": [ + { "type": "dateRange" } + ] +} diff --git a/schemas/low-code/definitions/dashboard-list-descriptor.schema.json b/schemas/low-code/definitions/dashboard-list-descriptor.schema.json new file mode 100644 index 0000000000..b7340c4118 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-list-descriptor.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-list-descriptor.schema.json", + "title": "DashboardListDescriptor", + "description": "Configuration for a dashboard list/table visualization.", + "markdownDescription": "AI guidance: use list visualizations for recent records, top records, or compact operational queues. `fields` must contain property names from the parent visualization entityName. Use `sortBy` to make results deterministic.", + "type": "object", + "properties": { + "fields": { + "type": "array", + "items": { "type": "string" }, + "description": "Property names to display as columns. Each value must reference a property on the visualization entityName." + }, + "sortBy": { + "type": "object", + "properties": { + "property": { + "type": "string", + "description": "Property name to sort by. Must exist on the visualization entityName." + }, + "direction": { + "type": "string", + "enum": ["asc", "desc"], + "default": "asc" + } + }, + "required": ["property"], + "additionalProperties": false + }, + "maxRows": { + "type": "integer", + "minimum": 1, + "maximum": 50, + "default": 10 + }, + "rowHeight": { + "type": "string", + "enum": ["compact", "normal", "tall"], + "default": "compact" + }, + "colorBy": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["property", "conditions"] + }, + "property": { + "type": "string", + "description": "Enum/status property name for automatic coloring." + } + }, + "required": ["type"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "required": ["fields"], + "additionalProperties": false, + "examples": [ + { + "fields": ["Title", "StartDate", "Status"], + "sortBy": { "property": "StartDate", "direction": "desc" }, + "maxRows": 10, + "rowHeight": "compact", + "colorBy": { "type": "property", "property": "Status" } + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-number-container-descriptor.schema.json b/schemas/low-code/definitions/dashboard-number-container-descriptor.schema.json new file mode 100644 index 0000000000..5cac8372a9 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-number-container-descriptor.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-number-container-descriptor.schema.json", + "title": "DashboardNumberContainerDescriptor", + "description": "A container that holds multiple number/KPI items, each with its own entity and aggregation.", + "markdownDescription": "AI guidance: use numberContainer for KPI tiles. Each item chooses its own entity, aggregation, optional filter, format, and color.", + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Number/KPI items within this container. Use 1-4 items for a readable dashboard row.", + "items": { + "$ref": "dashboard-number-item-descriptor.schema.json" + } + } + }, + "required": ["items"], + "additionalProperties": false, + "examples": [ + { + "items": [ + { "name": "total-events", "title": "Total Events", "entityName": "Acme.Events.Event", "aggregation": "count", "format": "number" } + ] + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-number-item-descriptor.schema.json b/schemas/low-code/definitions/dashboard-number-item-descriptor.schema.json new file mode 100644 index 0000000000..eb98b09ac4 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-number-item-descriptor.schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-number-item-descriptor.schema.json", + "title": "DashboardNumberItemDescriptor", + "description": "A single number/KPI tile within a number container.", + "markdownDescription": "AI guidance: `count` does not need aggregationProperty. `sum`, `average`, `min`, `max`, `percentFilled`, and `percentEmpty` should specify aggregationProperty. Use filter to scope the KPI, for example count only active records.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique identifier for this number item. Prefer kebab-case such as 'total-events'.", + "minLength": 1 + }, + "title": { + "type": "string", + "description": "Display title for this KPI." + }, + "entityName": { + "type": "string", + "description": "Entity this number item sources data from. Must match an entity descriptor or known reference entity.", + "minLength": 1 + }, + "aggregation": { + "$ref": "dashboard-aggregation-type.schema.json", + "description": "Aggregation used to compute the KPI value." + }, + "aggregationProperty": { + "type": "string", + "description": "Property name for sum/average/min/max/percent aggregations. Omit for count." + }, + "format": { + "type": "string", + "enum": ["number", "currency", "percentage"], + "default": "number" + }, + "color": { + "type": "string", + "description": "Display color name (blue, green, red, purple, orange, indigo, amber, teal)" + }, + "globalDateFilterProperty": { + "type": "string", + "description": "Date/DateTime property used to apply dashboard global date filters. Defaults to CreationTime when omitted." + }, + "filter": { + "oneOf": [ + { "$ref": "dashboard-filter-descriptor.schema.json" }, + { "type": "null" } + ] + }, + "clickToSeeRecords": { + "type": "boolean", + "description": "Allow users to click to see underlying records when supported by the runtime.", + "default": false + } + }, + "required": ["name", "title", "entityName", "aggregation"], + "additionalProperties": false, + "examples": [ + { + "name": "total-events", + "title": "Total Events", + "entityName": "Acme.Events.Event", + "aggregation": "count", + "format": "number", + "color": "blue" + }, + { + "name": "total-budget", + "title": "Total Budget", + "entityName": "Acme.Events.Event", + "aggregation": "sum", + "aggregationProperty": "Budget", + "format": "currency", + "color": "green" + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-visualization-descriptor.schema.json b/schemas/low-code/definitions/dashboard-visualization-descriptor.schema.json new file mode 100644 index 0000000000..5634498f74 --- /dev/null +++ b/schemas/low-code/definitions/dashboard-visualization-descriptor.schema.json @@ -0,0 +1,184 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-visualization-descriptor.schema.json", + "title": "DashboardVisualizationDescriptor", + "description": "A single visualization element in a dashboard: chart, list, or number container. Positioned on the grid via row, order, and width.", + "markdownDescription": "AI guidance: set `type` and then provide the matching payload: `chart` for type chart, `list` for type list, or `numberContainer` for type numberContainer. Do not populate unrelated payloads. Chart and list visualizations require `entityName`; number containers define entityName per KPI item.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique identifier within the dashboard. Prefer kebab-case such as 'events-by-status'.", + "minLength": 1 + }, + "type": { + "$ref": "dashboard-visualization-type.schema.json", + "description": "Visualization renderer type. Determines which payload property must be populated." + }, + "title": { + "type": "string", + "description": "Display title for the visualization." + }, + "description": { + "type": ["string", "null"], + "description": "Optional description text. Can be shown inline or as tooltip depending on showDescriptionAsTooltip." + }, + "row": { + "type": "integer", + "description": "Zero-based visual row index inside the dashboard grid. Visualizations sharing the same row are rendered side-by-side.", + "minimum": 0, + "default": 0 + }, + "order": { + "type": ["integer", "null"], + "description": "Order of the visualization inside its row. When omitted, array order is used." + }, + "width": { + "type": "integer", + "enum": [1, 2], + "default": 1, + "description": "Column width: 1 = half row, 2 = full row." + }, + "entityName": { + "type": "string", + "description": "Entity this visualization sources data from. Required for chart and list visualizations; numberContainer items define their own entityName." + }, + "globalDateFilterProperty": { + "type": "string", + "description": "Date/DateTime property used to apply dashboard global date filters to this visualization. Defaults to CreationTime when omitted." + }, + "filter": { + "oneOf": [ + { "$ref": "dashboard-filter-descriptor.schema.json" }, + { "type": "null" } + ] + }, + "userFilters": { + "type": "array", + "description": "Interactive filters exposed to end users on this visualization.", + "items": { + "type": "object", + "properties": { + "property": { + "type": "string", + "description": "Property name to filter on. Must exist on entityName.", + "minLength": 1 + } + }, + "required": ["property"], + "additionalProperties": false + } + }, + "showDescriptionAsTooltip": { + "type": "boolean", + "default": false + }, + "clickToSeeRecords": { + "type": "boolean", + "description": "Allow users to click to see underlying records when supported by the runtime.", + "default": false + }, + "chart": { + "oneOf": [ + { "$ref": "dashboard-chart-descriptor.schema.json" }, + { "type": "null" } + ] + }, + "list": { + "oneOf": [ + { "$ref": "dashboard-list-descriptor.schema.json" }, + { "type": "null" } + ] + }, + "numberContainer": { + "oneOf": [ + { "$ref": "dashboard-number-container-descriptor.schema.json" }, + { "type": "null" } + ] + } + }, + "required": ["name", "type", "title"], + "allOf": [ + { + "if": { + "required": ["type"], + "properties": { + "type": { "enum": ["chart", "Chart"] } + } + }, + "then": { + "required": ["chart", "entityName"], + "properties": { + "chart": { "$ref": "dashboard-chart-descriptor.schema.json" }, + "entityName": { + "type": "string", + "minLength": 1 + } + } + } + }, + { + "if": { + "required": ["type"], + "properties": { + "type": { "enum": ["list", "List"] } + } + }, + "then": { + "required": ["list", "entityName"], + "properties": { + "list": { "$ref": "dashboard-list-descriptor.schema.json" }, + "entityName": { + "type": "string", + "minLength": 1 + } + } + } + }, + { + "if": { + "required": ["type"], + "properties": { + "type": { "enum": ["numberContainer", "NumberContainer"] } + } + }, + "then": { + "required": ["numberContainer"], + "properties": { + "numberContainer": { "$ref": "dashboard-number-container-descriptor.schema.json" } + } + } + } + ], + "additionalProperties": false, + "examples": [ + { + "name": "events-by-status", + "type": "chart", + "title": "Events by Status", + "row": 0, + "order": 0, + "width": 1, + "entityName": "Acme.Events.Event", + "chart": { + "chartType": "bar", + "xAxis": { "property": "Status" }, + "yAxis": [{ "aggregation": "count", "label": "Events" }] + } + }, + { + "name": "recent-events", + "type": "list", + "title": "Recent Events", + "row": 0, + "order": 1, + "width": 1, + "entityName": "Acme.Events.Event", + "list": { + "fields": ["Title", "StartDate", "Status"], + "sortBy": { "property": "StartDate", "direction": "desc" }, + "maxRows": 10 + } + } + ] +} diff --git a/schemas/low-code/definitions/dashboard-visualization-type.schema.json b/schemas/low-code/definitions/dashboard-visualization-type.schema.json new file mode 100644 index 0000000000..0f490d6cdf --- /dev/null +++ b/schemas/low-code/definitions/dashboard-visualization-type.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "dashboard-visualization-type.schema.json", + "title": "DashboardVisualizationType", + "description": "The type of dashboard visualization. Prefer lower-case values in descriptor JSON; PascalCase aliases are accepted for compatibility.", + "markdownDescription": "`chart` groups/aggregates entity data into a bar/line/pie/donut chart. `list` displays recent/top records from one entity. `numberContainer` displays one or more KPI number tiles, each with its own entity and aggregation.", + "type": "string", + "enum": ["chart", "Chart", "list", "List", "numberContainer", "NumberContainer"], + "enumDescriptions": [ + "Chart visualization with x/y axis aggregation.", + "Chart visualization with x/y axis aggregation.", + "Record list visualization.", + "Record list visualization.", + "KPI number/tile container.", + "KPI number/tile container." + ] +} diff --git a/schemas/low-code/definitions/endpoint-descriptor.schema.json b/schemas/low-code/definitions/endpoint-descriptor.schema.json new file mode 100644 index 0000000000..cdbfaa18fb --- /dev/null +++ b/schemas/low-code/definitions/endpoint-descriptor.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "endpoint-descriptor.schema.json", + "title": "Custom Endpoint Descriptor", + "description": "Defines a custom HTTP endpoint that executes server-side JavaScript code.", + "markdownDescription": "AI guidance: use custom endpoints for model-owned actions and lightweight APIs. `name` must be unique. `route` should start with `/api/` and must not conflict with another route/method. Use `{id}` style path parameters when needed. Scripts can access request data through the endpoint context and return HTTP results with helpers such as `context.ok(value)`, `context.created(value)`, and `context.noContent()` where available. Require authentication by default and add `requiredPermissions` for protected operations.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique endpoint identifier used by designer/model health. Prefer PascalCase or kebab-case, for example 'SearchCustomers'.", + "minLength": 1 + }, + "route": { + "type": "string", + "description": "URL route pattern. Must start with '/' and should use an application-specific prefix such as '/api/low-code/events/{id}'. Route parameters use ASP.NET style braces, for example '{id}'.", + "minLength": 1, + "pattern": "^/" + }, + "method": { + "type": "string", + "description": "HTTP method. GET should be read-only; POST/PUT/PATCH/DELETE may mutate state.", + "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"], + "default": "GET" + }, + "javascript": { + "type": "string", + "description": "JavaScript code to execute. Use context request/response helpers and services exposed by the host, such as db, currentUser/currentTenant, authorization, emailSender, config, http, event bus, background jobs, and logging helpers.", + "minLength": 1 + }, + "requireAuthentication": { + "type": "boolean", + "description": "Whether authentication is required. Keep true unless this endpoint is intentionally public.", + "default": true + }, + "requiredPermissions": { + "type": "array", + "description": "Permission names required to access the endpoint. Checked only when authentication is required. Values should reference custom permissions or known static permissions.", + "items": { + "type": "string" + } + }, + "description": { + "type": "string", + "description": "Optional human-readable description for designer documentation and model health context." + } + }, + "required": ["name", "route", "javascript"], + "additionalProperties": false, + "examples": [ + { + "name": "SearchCustomers", + "route": "/api/low-code/customers/search", + "method": "GET", + "requireAuthentication": true, + "requiredPermissions": ["Acme.Customers.View"], + "javascript": "var q = context.request.query.q || ''; var table = await db.query('Acme.Crm.Customer'); var rows = await table.where(c => c.Name.toLowerCase().includes(q.toLowerCase())).take(10).toList(); return context.ok(rows);" + } + ] +} diff --git a/schemas/low-code/definitions/entity-attachment-descriptor.schema.json b/schemas/low-code/definitions/entity-attachment-descriptor.schema.json new file mode 100644 index 0000000000..bf1be2fad4 --- /dev/null +++ b/schemas/low-code/definitions/entity-attachment-descriptor.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "entity-attachment-descriptor.schema.json", + "title": "EntityAttachmentDescriptor", + "description": "Configures record-level attachments for an entity.", + "markdownDescription": "AI guidance: use entity attachments when records need a collection of arbitrary files. Use File/Image entity properties instead when the file is a named business field such as `ContractPdf` or `CoverImage`.", + "type": "object", + "properties": { + "isEnabled": { + "type": "boolean", + "description": "When true, records of this entity can have attachments." + }, + "maxFileCount": { + "type": "integer", + "description": "Maximum number of files allowed per entity record.", + "minimum": 1 + }, + "maxFileSizeBytes": { + "type": "integer", + "description": "Maximum allowed size in bytes for a single attachment.", + "minimum": 1 + }, + "maxTotalSizeBytes": { + "type": "integer", + "description": "Maximum total attachment size in bytes per entity record.", + "minimum": 1 + }, + "allowedContentTypes": { + "type": "array", + "description": "Allowed MIME content types, wildcard MIME patterns, or file extensions. Examples: 'image/*', 'application/pdf', '.docx'.", + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "required": ["isEnabled"], + "additionalProperties": false, + "examples": [ + { + "isEnabled": true, + "maxFileCount": 5, + "maxFileSizeBytes": 5242880, + "allowedContentTypes": ["image/*", "application/pdf"] + } + ] +} diff --git a/schemas/low-code/definitions/entity-cross-field-validation-descriptor.schema.json b/schemas/low-code/definitions/entity-cross-field-validation-descriptor.schema.json new file mode 100644 index 0000000000..5fff4ea579 --- /dev/null +++ b/schemas/low-code/definitions/entity-cross-field-validation-descriptor.schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "entity-cross-field-validation-descriptor.schema.json", + "title": "EntityCrossFieldValidationDescriptor", + "description": "Describes an entity-level validation rule that compares one property against another property on the same entity.", + "markdownDescription": "AI guidance: use cross-field validations for rules such as EndDate > StartDate, Min <= Max, PasswordRepeat == Password, or PublishedOn >= CreatedOn. `propertyName` receives the validation error; `otherPropertyName` is the comparison target. Both must exist on the entity.", + "type": "object", + "properties": { + "propertyName": { + "type": "string", + "description": "Property that receives the validation error when the rule fails. Must exist on the entity.", + "minLength": 1 + }, + "operator": { + "type": "string", + "description": "Comparison operator", + "enum": [ + "equals", + "notEquals", + "greaterThan", + "greaterThanOrEqual", + "lessThan", + "lessThanOrEqual" + ] + }, + "otherPropertyName": { + "type": "string", + "description": "Property to compare against. Must exist on the same entity.", + "minLength": 1 + }, + "message": { + "type": "string", + "description": "Optional custom validation message" + } + }, + "required": ["propertyName", "operator", "otherPropertyName"], + "additionalProperties": false, + "examples": [ + { + "propertyName": "EndDate", + "operator": "greaterThan", + "otherPropertyName": "StartDate", + "message": "End Date must be greater than Start Date." + }, + { + "propertyName": "PasswordRepeat", + "operator": "equals", + "otherPropertyName": "Password", + "message": "Password repeat must match Password." + } + ] +} diff --git a/schemas/low-code/definitions/entity-descriptor.schema.json b/schemas/low-code/definitions/entity-descriptor.schema.json new file mode 100644 index 0000000000..175966db3a --- /dev/null +++ b/schemas/low-code/definitions/entity-descriptor.schema.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "entity-descriptor.schema.json", + "title": "EntityDescriptor", + "description": "Describes a dynamic entity. An entity is the runtime data model: it defines a table-like aggregate, its properties, validations, parent-child relation, attachment support, and create/update/delete interceptors.", + "markdownDescription": "AI guidance: create one entity per aggregate/root concept. Use a stable namespace-style `name` such as `Acme.Crm.Customer`; this name is referenced by pages, forms, foreign keys, dashboards, scripts, and permissions. Put user-facing labels in `displayName`; choose `displayProperty` as a short string property used by lookups. Use `parent` only for child/detail entities that belong to a parent record.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Stable full name of the entity, usually Namespace.Module.EntityName (for example 'Acme.Crm.Customer'). Must be unique across all model layers. Do not rename after data exists unless a migration/rename flow is intended.", + "minLength": 1 + }, + "displayName": { + "type": "string", + "description": "Default plural/screen label for this entity (for example 'Customers'). Page menu titles are configured separately on page descriptors.", + "minLength": 1 + }, + "displayProperty": { + "type": "string", + "description": "Property name used when another entity references this entity in a lookup/autocomplete. Prefer a required string property such as 'Name', 'Title', or 'Code'." + }, + "parent": { + "type": "string", + "description": "Full name of the parent entity for parent-child/detail entities. When set, records of this entity are scoped under the parent and should include an FK property to the parent.", + "minLength": 1 + }, + "attachments": { + "$ref": "entity-attachment-descriptor.schema.json", + "description": "Record-level attachment settings. Use this for multiple arbitrary files attached to a record; use File/Image properties for first-class file fields." + }, + "properties": { + "type": "array", + "description": "Entity properties. Omit framework audit/id fields such as Id, CreationTime, CreatorId, LastModificationTime, IsDeleted unless intentionally overriding metadata; the runtime supplies standard fields.", + "items": { + "$ref": "entity-property-descriptor.schema.json" + } + }, + "crossFieldValidations": { + "type": "array", + "description": "Entity-level validation rules that compare two properties from the same entity. Use for date ranges, matching password fields, numeric min/max pairs, and other cross-field rules.", + "items": { + "$ref": "entity-cross-field-validation-descriptor.schema.json" + } + }, + "interceptors": { + "type": "array", + "description": "Create/Update/Delete command interceptors. Use Pre interceptors to validate, normalize, or block a command; use Post interceptors for side effects after persistence.", + "items": { + "$ref": "command-interceptor-descriptor.schema.json" + } + } + }, + "required": ["name"], + "additionalProperties": false, + "examples": [ + { + "name": "Acme.Crm.Customer", + "displayName": "Customers", + "displayProperty": "Name", + "properties": [ + { "name": "Name", "type": "string", "isRequired": true }, + { "name": "Email", "type": "string", "validators": [{ "type": "email" }] } + ] + } + ] +} diff --git a/schemas/low-code/definitions/entity-property-descriptor.schema.json b/schemas/low-code/definitions/entity-property-descriptor.schema.json new file mode 100644 index 0000000000..ed4cc8da06 --- /dev/null +++ b/schemas/low-code/definitions/entity-property-descriptor.schema.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "entity-property-descriptor.schema.json", + "title": "EntityPropertyDescriptor", + "description": "Describes one dynamic entity property. Properties define persisted data fields, enum fields, foreign keys, upload fields, server-only fields, defaults, uniqueness, and validators.", + "markdownDescription": "AI guidance: use PascalCase property names. Use `type` for primitive fields, `type: \"enum\"` with `enumType` for enum fields, and a `foreignKey` object for lookup fields. FK property names should normally end with `Id`. Keep sensitive values `serverOnly: true`. Do not add legacy UI configuration here; page columns/filters and form fields own UI behavior.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Stable PascalCase property name, unique within the entity. Examples: 'Name', 'EmailAddress', 'CustomerId', 'StartDate'.", + "minLength": 1 + }, + "type": { + "$ref": "entity-property-type.schema.json", + "description": "Primitive or special property type. If omitted, runtime treats the property as string. Use 'enum' only with enumType; use 'file'/'image' for first-class upload fields." + }, + "displayName": { + "type": "string", + "description": "Default display label for this property. Page columns and form fields can override it. Omit when the label can be derived from the property name.", + "minLength": 1 + }, + "enumType": { + "type": "string", + "description": "Name of a JSON-defined enum from top-level enums or a full type name for code enums. Required when type is 'enum'." + }, + "allowSetByClients": { + "type": "boolean", + "description": "Controls whether create/update clients may set the property. Use false for server-computed or protected fields that can still be returned to clients." + }, + "serverOnly": { + "type": "boolean", + "description": "When true, this property is completely hidden from clients, API responses, and UI definitions. Use for secrets, internal notes, hashes, or backend-only workflow state." + }, + "isMappedToDbField": { + "type": "boolean", + "description": "Whether this property is mapped to a database column. Keep true or omit for normal persisted fields. Use false for computed/transient fields supplied by scripts or backend logic." + }, + "defaultValue": { + "type": ["string", "null"], + "description": "Default value for new records. Stored as a string in descriptor JSON and converted to the declared property type at runtime. Examples: '0' for int/enum, 'true' for boolean, '2026-05-18T09:00:00Z' for datetime." + }, + "isUnique": { + "type": "boolean", + "description": "Whether this property value must be unique across records of this entity. Use for codes, slugs, natural keys, and names only when duplicates are not allowed." + }, + "isRequired": { + "type": "boolean", + "description": "When true, the property is required/not nullable. This affects database schema, backend validation, and generated UI validation. Existing data may need defaults before turning this on." + }, + "foreignKey": { + "$ref": "foreign-key-descriptor.schema.json", + "description": "Foreign key/lookup relation. The current property's value stores the referenced entity id. The property name should usually be 'NameId', for example 'CustomerId'." + }, + "fileMaxSizeBytes": { + "type": "integer", + "description": "Maximum allowed file size in bytes for File and Image properties. Example: 5242880 for 5 MiB.", + "minimum": 1 + }, + "fileAllowedContentTypes": { + "type": "array", + "description": "Allowed MIME content types or wildcard patterns for File and Image properties. Examples: 'image/*', 'application/pdf'.", + "items": { + "type": "string", + "minLength": 1 + } + }, + "imageMaxWidth": { + "type": "integer", + "description": "Optional maximum image width in pixels.", + "minimum": 1 + }, + "imageMaxHeight": { + "type": "integer", + "description": "Optional maximum image height in pixels.", + "minimum": 1 + }, + "imageResizeMode": { + "type": "string", + "description": "How uploaded images should be resized when image dimensions are configured. 'fit' preserves the full image inside the bounds; 'fill' crops/fills the target bounds.", + "enum": ["fit", "fill"] + }, + "validators": { + "type": "array", + "description": "Backend/UI validators for this property. Use required, length, range, pattern, email, phone, url, or creditCard as appropriate. Duplicate required can be omitted when isRequired is true unless a custom message is needed.", + "items": { + "$ref": "validator-descriptor.schema.json" + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "examples": [ + { "name": "Name", "type": "string", "isRequired": true, "validators": [{ "type": "maxLength", "length": 128 }] }, + { "name": "Status", "type": "enum", "enumType": "Acme.Events.EventStatus", "defaultValue": "0" }, + { "name": "CustomerId", "foreignKey": { "entityName": "Acme.Crm.Customer", "displayPropertyName": "Name" } }, + { "name": "CoverImage", "type": "image", "fileAllowedContentTypes": ["image/*"], "fileMaxSizeBytes": 5242880, "imageResizeMode": "fit" } + ] +} diff --git a/schemas/low-code/definitions/entity-property-type.schema.json b/schemas/low-code/definitions/entity-property-type.schema.json new file mode 100644 index 0000000000..89d9018446 --- /dev/null +++ b/schemas/low-code/definitions/entity-property-type.schema.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "entity-property-type.schema.json", + "title": "EntityPropertyType", + "description": "Data type of an entity property. Use canonical lowercase values for new descriptors and MCP mutations; compatibility aliases are accepted for older descriptors.", + "markdownDescription": "Use canonical lowercase values in MCP/write payloads: `string`, `int`, `long`, `decimal`, `datetime`, `boolean`, `guid`, `enum`, `date`, `time`, `file`, `image`, or `money`. `string` stores text; `int`/`long` store whole numbers; `decimal` and `money` store numeric decimals; `datetime`, `date`, and `time` model temporal values; `boolean` stores true/false; `guid` stores GUID values; `enum` requires `enumType`; `file` and `image` store upload metadata/content through the low-code file pipeline. Legacy aliases such as `String`, `Int`, `dateTime`, and `DateTime` remain accepted by the schema for compatibility, but new payloads should not use .NET aliases like `Int32`.", + "type": "string", + "enum": [ + "string", + "String", + "int", + "Int", + "long", + "Long", + "decimal", + "Decimal", + "datetime", + "dateTime", + "DateTime", + "boolean", + "Boolean", + "guid", + "Guid", + "enum", + "Enum", + "date", + "Date", + "time", + "Time", + "file", + "File", + "image", + "Image", + "money", + "Money" + ], + "enumDescriptions": [ + "Text/string value.", + "Text/string value.", + "32-bit whole number.", + "32-bit whole number.", + "64-bit whole number.", + "64-bit whole number.", + "Decimal numeric value.", + "Decimal numeric value.", + "Date and time value.", + "Date and time value.", + "Date and time value.", + "Boolean true/false value.", + "Boolean true/false value.", + "GUID/UUID value.", + "GUID/UUID value.", + "Integer-backed enum value; set enumType.", + "Integer-backed enum value; set enumType.", + "Date-only value.", + "Date-only value.", + "Time-only value.", + "Time-only value.", + "Uploaded file field.", + "Uploaded file field.", + "Uploaded image field with optional dimension constraints.", + "Uploaded image field with optional dimension constraints.", + "Money amount rendered with money-aware controls.", + "Money amount rendered with money-aware controls." + ] +} diff --git a/schemas/low-code/definitions/enum-descriptor.schema.json b/schemas/low-code/definitions/enum-descriptor.schema.json new file mode 100644 index 0000000000..4a70d5c8f0 --- /dev/null +++ b/schemas/low-code/definitions/enum-descriptor.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "enum-descriptor.schema.json", + "title": "EnumDescriptor", + "description": "Describes an integer-backed enum definition for use in entity properties and select/kanban UI.", + "markdownDescription": "AI guidance: define enums before entity properties that reference them. Use a stable namespace-style `name` such as `Acme.Events.EventStatus`. Each value name should be PascalCase and unique within the enum. Use explicit integer `value`s for stable persistence; do not reorder or renumber after data exists.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Stable unique enum name. Use a namespace-style name if the enum belongs to a module/domain, for example 'Acme.Events.EventStatus'.", + "minLength": 1 + }, + "values": { + "type": "array", + "description": "Ordered list of integer-backed enum values. Values are stored as integers; names are used for display and model readability.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "PascalCase enum value name, for example 'Draft', 'Scheduled', or 'Completed'." + }, + "value": { + "type": "integer", + "description": "Stable integer value. Prefer explicit values starting at 0 so generated descriptor JSON is deterministic." + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "minItems": 1 + } + }, + "required": [ + "name", + "values" + ], + "additionalProperties": false, + "examples": [ + { + "name": "Acme.Events.EventStatus", + "values": [ + { "name": "Draft", "value": 0 }, + { "name": "Scheduled", "value": 1 }, + { "name": "Completed", "value": 2 } + ] + } + ] +} diff --git a/schemas/low-code/definitions/foreign-key-descriptor.schema.json b/schemas/low-code/definitions/foreign-key-descriptor.schema.json new file mode 100644 index 0000000000..358bdee4ee --- /dev/null +++ b/schemas/low-code/definitions/foreign-key-descriptor.schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "foreign-key-descriptor.schema.json", + "title": "ForeignKeyDescriptor", + "description": "Describes a foreign key/lookup relationship from the owning entity property to another entity or reference entity.", + "markdownDescription": "AI guidance: put `foreignKey` on the property that stores the related record id. The property name should usually end with `Id`, for example `CustomerId`. `entityName` must match a model entity name or a registered code/reference entity such as `Volo.Abp.Identity.IdentityUser`. Use `displayPropertyName` when the target display field is not obvious. Use `dependsOn` for cascading dropdowns such as City filtered by Country. Use `access` only for reverse access from the referenced entity side: `none`, `view`, or `edit`. Do not use `lookup` as `access`; lookup is a form field/control type.", + "type": "object", + "properties": { + "entityName": { + "type": "string", + "description": "Full name of the related entity or registered reference entity. Must match an entity descriptor name or a known code/reference entity.", + "minLength": 1 + }, + "displayPropertyName": { + "type": "string", + "description": "Property name to display from the related entity in lookups/autocomplete. Omit to use the target entity displayProperty.", + "minLength": 1 + }, + "access": { + "type": "string", + "description": "Access level for managing this relation from the referenced entity side. 'none' means no reverse access; 'view' allows the referenced entity page to show related records; 'edit' allows managing related records from the referenced side.", + "enum": ["none", "None", "view", "View", "edit", "Edit"], + "default": "none" + }, + "dependsOn": { + "type": "object", + "description": "Cascading dependency: filter this FK lookup by the value of another FK property on the same owning entity. Example: CityId depends on CountryId and filters City.CountryId.", + "properties": { + "propertyName": { + "type": "string", + "description": "Property name on the owning entity whose value provides the filter (for example 'CountryId' on an Address entity).", + "minLength": 1 + }, + "filterPropertyName": { + "type": "string", + "description": "Property name on the target lookup entity to filter by (for example 'CountryId' on City).", + "minLength": 1 + } + }, + "required": ["propertyName", "filterPropertyName"], + "additionalProperties": false + } + }, + "required": ["entityName"], + "additionalProperties": false, + "examples": [ + { + "entityName": "Acme.Crm.Customer", + "displayPropertyName": "Name" + }, + { + "entityName": "Acme.Geo.City", + "displayPropertyName": "Name", + "dependsOn": { + "propertyName": "CountryId", + "filterPropertyName": "CountryId" + } + } + ] +} diff --git a/schemas/low-code/definitions/form-descriptor.schema.json b/schemas/low-code/definitions/form-descriptor.schema.json new file mode 100644 index 0000000000..102193ee10 --- /dev/null +++ b/schemas/low-code/definitions/form-descriptor.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "form-descriptor.schema.json", + "title": "FormDescriptor", + "description": "Describes a named create/edit form definition bound to one entity.", + "markdownDescription": "AI guidance: a form is referenced by page `formName`, `createFormName`, or `editFormName`. Keep `entityName` aligned with the page entityName. Define all fields in `fields`, then place every visible field in `layout.tabs[].groups[].fields[]` by field id (each placement carries `row`, `colSpan`, and `colStart`). Use form `rules` for conditional visibility/enabled state; use entity validators for core data validation.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Stable unique form identifier. Prefer kebab-case, for example 'customer-form' or 'event-form'. Pages reference this value.", + "minLength": 1 + }, + "entityName": { + "type": "string", + "description": "Full name of the entity this form is bound to. Must match the page entityName that uses this form.", + "minLength": 1 + }, + "enableSaveAndNew": { + "type": "boolean", + "description": "Whether the form should expose a Save and New action in addition to the standard save action. Useful for rapid data entry.", + "default": false + }, + "fields": { + "type": "array", + "description": "Flat list of all fields in this form. Field ids must be unique within the form; bound fields should point to properties on entityName.", + "items": { + "$ref": "form-field-descriptor.schema.json" + } + }, + "layout": { + "$ref": "form-layout-descriptor.schema.json", + "description": "Visual layout for the fields. Every layout placement fieldId must refer to a field in fields." + }, + "rules": { + "type": "array", + "description": "Conditional rules for field/group visibility, enabled state, and value setting. Use for simple client-side form behavior.", + "items": { + "$ref": "form-rule-descriptor.schema.json" + } + } + }, + "required": ["name", "entityName", "fields", "layout"], + "additionalProperties": false, + "examples": [ + { + "name": "event-form", + "entityName": "Acme.Events.Event", + "fields": [ + { "id": "title", "label": "Title", "type": "text", "binding": "Title" }, + { "id": "status", "label": "Status", "type": "select", "binding": "Status", "enumType": "Acme.Events.EventStatus" } + ], + "layout": { + "tabs": [ + { + "id": "main", + "title": "Main", + "isDefault": true, + "groups": [ + { + "id": "details", + "title": "Details", + "isDefault": true, + "fields": [ + { "fieldId": "title", "row": 0, "colSpan": 4 }, + { "fieldId": "status", "row": 1, "colSpan": 2 } + ] + } + ] + } + ] + } + } + ] +} diff --git a/schemas/low-code/definitions/form-field-descriptor.schema.json b/schemas/low-code/definitions/form-field-descriptor.schema.json new file mode 100644 index 0000000000..4875100078 --- /dev/null +++ b/schemas/low-code/definitions/form-field-descriptor.schema.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "form-field-descriptor.schema.json", + "title": "FormFieldDescriptor", + "description": "Describes a single field in a form. A field may be bound to an entity property or unbound for computed/display-only UI.", + "markdownDescription": "AI guidance: use a stable camelCase `id` for each field. For ordinary data entry, set `binding` to an entity property and choose a field `type` compatible with that property. For enum selects, set `type: \"select\"` and `enumType`. For FK lookups, use `type: \"lookup\"` and bind to the FK property. Place fields into the layout by adding a placement that references this id in a group's `fields`.", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this field within the form. Prefer camelCase, for example 'title' or 'customerId'. Layout placements and rules reference this id.", + "minLength": 1 + }, + "label": { + "type": "string", + "description": "Display label for the field.", + "minLength": 1 + }, + "type": { + "$ref": "form-field-type.schema.json", + "description": "Visual/input control type. Choose a type compatible with the bound entity property." + }, + "binding": { + "type": ["string", "null"], + "description": "Entity property name to bind to, or null/omitted for unbound fields. Supports dotted paths like 'Parent.Name' for related entity display where supported." + }, + "enumType": { + "type": "string", + "description": "Enum name for select fields bound to enum properties. Must match the entity property's enumType or a known code enum." + }, + "defaultValue": { + "description": "Default field value used by the UI when creating a new record. Prefer entity property defaultValue for persisted defaults." + }, + "placeholder": { + "type": "string", + "description": "Placeholder text for the input" + }, + "helpText": { + "type": "string", + "description": "Help text displayed below the field" + }, + "readOnly": { + "type": "boolean", + "description": "Whether the field is read-only in the form UI. This does not by itself protect backend writes; use allowSetByClients/serverOnly for security.", + "default": false + }, + "modeVisibility": { + "type": "string", + "enum": ["both", "Both", "createOnly", "CreateOnly", "editOnly", "EditOnly"], + "description": "Controls in which form mode the field is visible: both, createOnly, or editOnly.", + "default": "both" + }, + "validations": { + "type": "array", + "description": "Form-level validation rules that supplement entity-level validators. Use when validation is specific to this form.", + "items": { + "$ref": "validator-descriptor.schema.json" + } + } + }, + "required": ["id", "label", "type"], + "additionalProperties": false, + "examples": [ + { "id": "title", "label": "Title", "type": "text", "binding": "Title", "validations": [{ "type": "required" }] }, + { "id": "status", "label": "Status", "type": "select", "binding": "Status", "enumType": "Acme.Events.EventStatus" }, + { "id": "customerId", "label": "Customer", "type": "lookup", "binding": "CustomerId" } + ] +} diff --git a/schemas/low-code/definitions/form-field-type.schema.json b/schemas/low-code/definitions/form-field-type.schema.json new file mode 100644 index 0000000000..2b0c5630f2 --- /dev/null +++ b/schemas/low-code/definitions/form-field-type.schema.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "form-field-type.schema.json", + "title": "FormFieldType", + "description": "Available form field control types. Use canonical lowercase values for new descriptors and MCP mutations; compatibility aliases are accepted for older descriptors.", + "markdownDescription": "Use canonical lowercase values in MCP/write payloads: `text`, `textarea`, `number`, `checkbox`, `date`, `datetime`, `select`, `lookup`, `guid`, `computed`, `time`, `file`, `image`, or `money`. `text`/`textarea` are for strings, `number` for numeric values, `checkbox` for boolean, `date`/`datetime`/`time` for temporal values, `select` for enum values, `lookup` for foreign keys, `guid` for GUID input/display, `computed` for unbound calculated/display fields, `file` and `image` for upload fields, and `money` for money/decimal amount input. There is no `email` field type; use `text` plus validations where needed. Legacy PascalCase aliases remain accepted by the schema for compatibility.", + "type": "string", + "enum": [ + "text", + "Text", + "textarea", + "Textarea", + "number", + "Number", + "checkbox", + "Checkbox", + "date", + "Date", + "datetime", + "DateTime", + "select", + "Select", + "lookup", + "Lookup", + "guid", + "Guid", + "computed", + "Computed", + "time", + "Time", + "file", + "File", + "image", + "Image", + "money", + "Money" + ], + "enumDescriptions": [ + "Single-line text input.", + "Single-line text input.", + "Multi-line text input.", + "Multi-line text input.", + "Numeric input.", + "Numeric input.", + "Boolean checkbox.", + "Boolean checkbox.", + "Date-only input.", + "Date-only input.", + "Date and time input.", + "Date and time input.", + "Enum/select input.", + "Enum/select input.", + "Foreign key lookup/autocomplete input.", + "Foreign key lookup/autocomplete input.", + "GUID input/display.", + "GUID input/display.", + "Unbound computed/display field.", + "Unbound computed/display field.", + "Time-only input.", + "Time-only input.", + "File upload field.", + "File upload field.", + "Image upload field.", + "Image upload field.", + "Money amount input.", + "Money amount input." + ] +} diff --git a/schemas/low-code/definitions/form-layout-descriptor.schema.json b/schemas/low-code/definitions/form-layout-descriptor.schema.json new file mode 100644 index 0000000000..364c0423dd --- /dev/null +++ b/schemas/low-code/definitions/form-layout-descriptor.schema.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "form-layout-descriptor.schema.json", + "title": "FormLayoutDescriptor", + "description": "Describes the visual layout of a form as tabs and groups. Each group holds a flat list of field placements positioned on a 4-column grid.", + "markdownDescription": "AI guidance: the layout is a tree: tabs -> groups -> fields. The stored schema is flat (no row/cell wrappers): each placement carries `row`, `colSpan`, and `colStart`. Rows are derived at render time by grouping placements that share the same zero-based `row`. Each placement `fieldId` must reference a field from the form's `fields` array. Use `colSpan` 4 for full-width fields, 2+2 for two columns (colStart 1 and 3), or 1+1+1+1 for four compact controls. The sum of `colSpan` values within a row should not exceed 4.", + "type": "object", + "properties": { + "tabs": { + "type": "array", + "description": "Ordered list of tabs in the form. Use one default tab named 'main' for simple forms.", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this tab. Prefer camelCase/kebab-case such as 'main' or 'advanced'.", + "minLength": 1 + }, + "title": { + "type": "string", + "description": "Display title for the tab", + "minLength": 1 + }, + "isDefault": { + "type": "boolean", + "description": "Whether this is the default tab. The designer uses the default tab as the safe target for orphaned fields.", + "default": false + }, + "order": { + "type": "integer", + "description": "Optional display order for this tab. When omitted, array order is used." + }, + "groups": { + "type": "array", + "description": "Ordered list of groups within this tab. Use one default group for simple forms.", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this group. Rules can target group ids.", + "minLength": 1 + }, + "title": { + "type": ["string", "null"], + "description": "Optional display title for the group. Use null or omit for an untitled group." + }, + "isDefault": { + "type": "boolean", + "description": "Whether this is the default group. The designer uses the default group as the safe target for orphaned fields.", + "default": false + }, + "order": { + "type": "integer", + "description": "Optional display order for this group. When omitted, array order is used." + }, + "fields": { + "type": "array", + "description": "Flat list of field placements in this group. Each placement positions a single field on the 4-column grid via row, colSpan, and colStart.", + "items": { + "type": "object", + "properties": { + "fieldId": { + "type": "string", + "description": "Reference to a field id in the form's fields array. Every field shown in the layout needs a matching field descriptor.", + "minLength": 1 + }, + "row": { + "type": "integer", + "description": "Zero-based visual row index inside the group. Placements sharing the same row are rendered side-by-side.", + "minimum": 0, + "default": 0 + }, + "colSpan": { + "type": "integer", + "description": "Number of grid columns this field spans from 1 to 4. Use 4 for full-width fields.", + "minimum": 1, + "maximum": 4, + "default": 4 + }, + "colStart": { + "type": ["integer", "null"], + "description": "Starting grid column from 1 to 4. Omit or null to auto-place after the previous placement in the same row.", + "minimum": 1, + "maximum": 4 + } + }, + "required": ["fieldId"], + "additionalProperties": false + } + } + }, + "required": ["id", "fields"], + "additionalProperties": false + } + } + }, + "required": ["id", "title", "groups"], + "additionalProperties": false + } + } + }, + "required": ["tabs"], + "additionalProperties": false, + "examples": [ + { + "tabs": [ + { + "id": "main", + "title": "Main", + "isDefault": true, + "groups": [ + { + "id": "details", + "title": "Details", + "isDefault": true, + "fields": [ + { "fieldId": "title", "row": 0, "colSpan": 4 }, + { "fieldId": "startDate", "row": 1, "colSpan": 2, "colStart": 1 }, + { "fieldId": "endDate", "row": 1, "colSpan": 2, "colStart": 3 } + ] + } + ] + } + ] + } + ] +} diff --git a/schemas/low-code/definitions/form-rule-descriptor.schema.json b/schemas/low-code/definitions/form-rule-descriptor.schema.json new file mode 100644 index 0000000000..f558b6bb1d --- /dev/null +++ b/schemas/low-code/definitions/form-rule-descriptor.schema.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "form-rule-descriptor.schema.json", + "title": "FormRuleDescriptor", + "description": "Describes a conditional form rule with one or more actions that execute when the condition is met.", + "markdownDescription": "AI guidance: use rules for simple client-side behavior such as hiding a group when a checkbox is false, disabling a field after a status is selected, or setting a default value. `condition.fieldId` and every action `targetId` must reference existing field/group ids. Rules are not security boundaries; enforce sensitive behavior with backend validation/interceptors too.", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this rule. Prefer kebab-case or camelCase such as 'show-archive-reason'.", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "Human-readable name for this rule, used by designers/documentation." + }, + "condition": { + "type": "object", + "description": "The condition that triggers this rule. For isEmpty/isNotEmpty, omit value.", + "properties": { + "fieldId": { + "type": "string", + "description": "The field whose value is evaluated. Must match a field id in the same form.", + "minLength": 1 + }, + "operator": { + "type": "string", + "enum": ["equals", "notEquals", "isEmpty", "isNotEmpty"], + "description": "Comparison operator. Use equals/notEquals with value; use isEmpty/isNotEmpty without value." + }, + "value": { + "description": "The value to compare against (not used for isEmpty/isNotEmpty)" + } + }, + "required": ["fieldId", "operator"], + "additionalProperties": false + }, + "actions": { + "type": "array", + "description": "Actions to perform when the condition is met. Actions are executed in order.", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["hide", "show", "disable", "enable", "setValue"], + "description": "The action type. hide/show/disable/enable target fields or groups; setValue targets fields." + }, + "targetType": { + "type": "string", + "enum": ["field", "group"], + "description": "Whether the target is a field or a group." + }, + "targetId": { + "type": "string", + "description": "The id of the target field or group. Must exist in this form's fields or layout groups.", + "minLength": 1 + }, + "value": { + "description": "The value to set. Used only for setValue actions." + } + }, + "required": ["type", "targetType", "targetId"], + "additionalProperties": false + } + } + }, + "required": ["id", "condition", "actions"], + "additionalProperties": false, + "examples": [ + { + "id": "show-archive-reason", + "name": "Show archive reason", + "condition": { "fieldId": "status", "operator": "equals", "value": 3 }, + "actions": [ + { "type": "show", "targetType": "field", "targetId": "archiveReason" } + ] + } + ] +} diff --git a/schemas/low-code/definitions/interceptor-type.schema.json b/schemas/low-code/definitions/interceptor-type.schema.json new file mode 100644 index 0000000000..49ce6eca3a --- /dev/null +++ b/schemas/low-code/definitions/interceptor-type.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "interceptor-type.schema.json", + "title": "InterceptorType", + "description": "When an entity command interceptor runs. Prefer PascalCase values shown here.", + "markdownDescription": "`Pre` runs before the command is persisted and can block with `globalError`. `Post` runs after the command succeeds and is best for logging, notifications, publishing events, or enqueueing jobs. `Replace` is reserved for replacing command behavior where supported and should be avoided unless the host explicitly supports it.", + "type": "string", + "enum": [ + "Pre", + "pre", + "Post", + "post", + "Replace", + "replace" + ], + "enumDescriptions": [ + "Before persistence; can block with globalError.", + "Before persistence; can block with globalError.", + "After successful persistence; use for side effects.", + "After successful persistence; use for side effects.", + "Replace command behavior where supported.", + "Replace command behavior where supported." + ] +} diff --git a/schemas/low-code/definitions/page-column-descriptor.schema.json b/schemas/low-code/definitions/page-column-descriptor.schema.json new file mode 100644 index 0000000000..ebf0073e4a --- /dev/null +++ b/schemas/low-code/definitions/page-column-descriptor.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-column-descriptor.schema.json", + "title": "PageColumnDescriptor", + "description": "Describes a property displayed by an entity page list, card, calendar event, or gallery item.", + "markdownDescription": "AI guidance: use page columns/card fields to control runtime page visibility. Each `propertyName` must match a property on the page entity. Keep this separate from entity property definitions so the same entity can have different views on different pages.", + "type": "object", + "properties": { + "propertyName": { + "type": "string", + "description": "Property displayed by this column/card field. Must match a property on the page entityName.", + "minLength": 1 + }, + "label": { + "type": "string", + "description": "Optional page-specific label override. Omit to use the property displayName/name.", + "minLength": 1 + }, + "order": { + "type": "integer", + "description": "Display order. Lower values appear first.", + "default": 0 + }, + "exportOrder": { + "type": "integer", + "description": "Optional export order. Lower values appear first in Excel, CSV, download-link columns, and file bundles. Omit to reuse display order." + }, + "width": { + "type": "string", + "description": "Optional CSS width for tabular columns, for example '160px', '12rem', or '20%'.", + "minLength": 1 + }, + "visible": { + "type": "boolean", + "description": "Whether the field is visible by default on this page.", + "default": true + }, + "exportable": { + "type": "boolean", + "description": "Whether the field can be exported from this page.", + "default": true + } + }, + "required": ["propertyName"], + "additionalProperties": false, + "examples": [ + { "propertyName": "Title", "label": "Title", "order": 0, "exportOrder": 0, "width": "240px", "visible": true }, + { "propertyName": "InternalNotes", "visible": false, "exportable": false } + ] +} diff --git a/schemas/low-code/definitions/page-descriptor.schema.json b/schemas/low-code/definitions/page-descriptor.schema.json new file mode 100644 index 0000000000..475de3ad63 --- /dev/null +++ b/schemas/low-code/definitions/page-descriptor.schema.json @@ -0,0 +1,259 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-descriptor.schema.json", + "title": "PageDescriptor", + "description": "Describes a runtime UI page. Pages create menu items and choose how an entity or dashboard is rendered.", + "markdownDescription": "AI guidance: `name` is the stable route/menu key and should be URL-safe kebab-case (for example `customers` or `event-calendar`). `title` is user-facing. For entity pages, set `entityName` to an existing entity. Configure list/gallery/card visibility with `columns`, filtering with `filters`, and create/edit UI with form names. For `dashboard` pages, provide `dashboard` and omit entity-only fields unless intentionally supported by the runtime. Do not put page-only settings on entity properties.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Stable URL-safe page identifier. Prefer kebab-case, for example 'customers', 'event-calendar', or 'sales-dashboard'. This becomes part of the runtime route and is referenced by saveSuccessPageName.", + "minLength": 1 + }, + "title": { + "type": "string", + "description": "Display title for the menu item and page header.", + "minLength": 1 + }, + "icon": { + "type": "string", + "description": "FontAwesome icon class for the menu item, for example 'fa-solid fa-users'." + }, + "type": { + "$ref": "page-type.schema.json" + }, + "entityName": { + "type": "string", + "description": "Full name of the root entity this page displays. Required for dataGrid, kanban, calendar, gallery, and form pages. Usually omitted for dashboard pages.", + "minLength": 1 + }, + "groupByProperty": { + "type": "string", + "description": "Property name to group entities by. Required for kanban pages. Must reference a property on entityName, preferably an enum/status property with a small fixed set of values.", + "minLength": 1 + }, + "calendarStartProperty": { + "type": "string", + "description": "Date or DateTime property used as the start date for calendar pages. Required for calendar pages.", + "minLength": 1 + }, + "calendarEndProperty": { + "type": "string", + "description": "Optional Date or DateTime property used as the end date for calendar pages. Use when events can span a date range.", + "minLength": 1 + }, + "calendarTimeProperty": { + "type": "string", + "description": "Optional Time property used as the start time for calendar pages when the start date is date-only or time is stored separately.", + "minLength": 1 + }, + "calendarDurationProperty": { + "type": "string", + "description": "Optional numeric property used as duration in minutes for calendar pages. Use this instead of calendarEndProperty when records store duration.", + "minLength": 1 + }, + "galleryImageProperty": { + "type": "string", + "description": "Optional Image property used as the cover image for gallery pages. The property should have entity property type image.", + "minLength": 1 + }, + "defaultSortProperty": { + "type": "string", + "description": "Optional property used as the default sorting field when the client does not send explicit sorting. Must reference a property on entityName.", + "minLength": 1 + }, + "defaultSortDescending": { + "type": "boolean", + "description": "Whether the default sort property is sorted descending.", + "default": false + }, + "defaultFileExportMode": { + "type": "integer", + "enum": [0, 1, 2], + "description": "Default file export mode for file/image columns. 0 exports file names, 1 exports metadata columns, and 2 exports download-link columns." + }, + "allowFileBundleExport": { + "type": "boolean", + "description": "Whether file bundle export is available for this page.", + "default": true + }, + "columns": { + "type": "array", + "description": "Page-owned column/card field configuration. Use for dataGrid, kanban card fields, calendar event fields, and gallery card fields. Each propertyName must reference a property on entityName.", + "items": { + "$ref": "page-column-descriptor.schema.json" + } + }, + "filters": { + "type": "array", + "description": "Page-owned filter configuration for filterable page types. Each propertyName must reference a property on entityName. Use this instead of legacy property-level UI filter settings.", + "items": { + "$ref": "page-filter-descriptor.schema.json" + } + }, + "order": { + "type": "integer", + "description": "Menu sort order (lower values appear first)", + "default": 0 + }, + "formName": { + "type": "string", + "description": "Name of the form to render. Required when type is 'form'. Must match a form descriptor whose entityName matches this page entityName.", + "minLength": 1 + }, + "createFormName": { + "type": "string", + "description": "Name of the form to use when creating records from dataGrid, kanban, calendar, or gallery pages. Must match a form descriptor for the same entity." + }, + "editFormName": { + "type": "string", + "description": "Name of the form to use when editing records from dataGrid, kanban, calendar, or gallery pages. Must match a form descriptor for the same entity." + }, + "createFormDisplay": { + "type": "string", + "enum": ["modal", "Modal", "page", "Page"], + "description": "How to display the create form. Use 'modal' for quick CRUD; use 'page' for longer forms or when deep links are desired.", + "default": "modal" + }, + "editFormDisplay": { + "type": "string", + "enum": ["modal", "Modal", "page", "Page"], + "description": "How to display the edit form. Use 'modal' for quick CRUD; use 'page' for longer forms or when deep links are desired.", + "default": "modal" + }, + "saveSuccessNavigation": { + "type": "string", + "enum": ["stay", "page", "url"], + "description": "Navigation behavior after a standalone form page saves successfully. 'stay' keeps the user on the form page; 'page' navigates to saveSuccessPageName; 'url' navigates to saveSuccessUrl.", + "default": "stay" + }, + "saveSuccessPageName": { + "type": "string", + "description": "Existing page name to open after a form page saves successfully when saveSuccessNavigation is 'page'.", + "minLength": 1 + }, + "saveSuccessUrl": { + "type": "string", + "description": "URL or application path to open after a form page saves successfully when saveSuccessNavigation is 'url'.", + "minLength": 1 + }, + "dashboard": { + "$ref": "dashboard-descriptor.schema.json", + "description": "Dashboard layout and visualizations. Required only when type is dashboard." + }, + "group": { + "type": "string", + "description": "Name of the page group this page belongs to. Must match pageGroups[].name. Omit for root-level pages.", + "minLength": 1 + }, + "permissionConfig": { + "$ref": "page-permission-config.schema.json", + "description": "Optional page operation permission overrides. Omit operations to use generated defaults." + } + }, + "required": ["name", "title", "type"], + "allOf": [ + { + "if": { + "properties": { "type": { "enum": ["dataGrid", "DataGrid"] } } + }, + "then": { + "required": ["entityName"] + } + }, + { + "if": { + "properties": { "type": { "enum": ["kanban", "Kanban"] } } + }, + "then": { + "required": ["entityName", "groupByProperty"] + } + }, + { + "if": { + "properties": { "type": { "enum": ["calendar", "Calendar"] } } + }, + "then": { + "required": ["entityName", "calendarStartProperty"] + } + }, + { + "if": { + "properties": { "type": { "enum": ["gallery", "Gallery"] } } + }, + "then": { + "required": ["entityName"] + } + }, + { + "if": { + "properties": { "type": { "enum": ["form", "Form"] } } + }, + "then": { + "required": ["entityName", "formName"] + } + }, + { + "if": { + "properties": { "type": { "enum": ["dashboard", "Dashboard"] } } + }, + "then": { + "required": ["dashboard"] + } + } + ], + "additionalProperties": false, + "examples": [ + { + "name": "events", + "title": "Events", + "type": "dataGrid", + "entityName": "Acme.Events.Event", + "columns": [ + { "propertyName": "Title", "order": 0 }, + { "propertyName": "Status", "order": 1 } + ], + "filters": [ + { "propertyName": "Title", "control": "text", "defaultOperator": "contains" } + ], + "createFormName": "event-form", + "editFormName": "event-form" + }, + { + "name": "event-calendar", + "title": "Event Calendar", + "type": "calendar", + "entityName": "Acme.Events.Event", + "calendarStartProperty": "StartDate", + "calendarEndProperty": "EndDate" + }, + { + "name": "event-dashboard", + "title": "Event Dashboard", + "type": "dashboard", + "dashboard": { + "visualizations": [ + { + "name": "events-by-status", + "type": "chart", + "title": "Events by Status", + "row": 0, + "order": 0, + "width": 2, + "entityName": "Acme.Events.Event", + "chart": { + "chartType": "bar", + "xAxis": { "property": "Status" }, + "yAxis": [{ "aggregation": "count", "label": "Events" }] + } + } + ] + } + } + ] +} diff --git a/schemas/low-code/definitions/page-filter-descriptor.schema.json b/schemas/low-code/definitions/page-filter-descriptor.schema.json new file mode 100644 index 0000000000..ed63ba0653 --- /dev/null +++ b/schemas/low-code/definitions/page-filter-descriptor.schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-filter-descriptor.schema.json", + "title": "PageFilterDescriptor", + "description": "Describes a runtime filter control for an entity page.", + "markdownDescription": "AI guidance: each filter must point to a page entity property. Use `control: \"auto\"` unless a specific control is needed. Use text/contains for string search, range/between for numeric values, dateRange/timeRange for temporal values, select/multiSelect for enums, lookup for foreign keys, and exists for null checks. Do not use the legacy `operator` property; use `defaultOperator`.", + "type": "object", + "properties": { + "propertyName": { + "type": "string", + "description": "Property filtered by this control. Must match a property on the page entityName.", + "minLength": 1 + }, + "label": { + "type": "string", + "description": "Optional page-specific label override. Omit to use the property displayName/name.", + "minLength": 1 + }, + "order": { + "type": "integer", + "description": "Filter display order. Lower values appear first.", + "default": 0 + }, + "visible": { + "type": "boolean", + "description": "Whether the filter is shown at runtime", + "default": true + }, + "control": { + "type": "string", + "description": "Runtime control used by the filter. 'auto' selects based on property type; choose explicit controls for predictable generated UI.", + "enum": ["auto", "text", "range", "dateRange", "timeRange", "select", "multiSelect", "lookup", "exists"], + "default": "auto" + }, + "defaultOperator": { + "type": "string", + "description": "Default operator applied by this filter. Use 'contains' for text search, 'equal' for enum/FK exact matches, 'between' for ranges, and 'default' to let runtime choose.", + "enum": [ + "default", + "equal", + "notEqual", + "contains", + "notContains", + "startsWith", + "endsWith", + "greaterThan", + "greaterThanOrEqual", + "lessThan", + "lessThanOrEqual", + "between", + "hasValue", + "isNull", + "isNotNull", + "in", + "notIn" + ] + }, + "allowedOperators": { + "type": "array", + "description": "Operators the runtime filter UI allows users to choose. Omit to let the runtime choose based on property type/control.", + "items": { + "type": "string", + "enum": [ + "default", + "equal", + "notEqual", + "contains", + "notContains", + "startsWith", + "endsWith", + "greaterThan", + "greaterThanOrEqual", + "lessThan", + "lessThanOrEqual", + "between", + "hasValue", + "isNull", + "isNotNull", + "in", + "notIn" + ] + }, + "uniqueItems": true + }, + "defaultValue": { + "description": "Optional default filter value applied when the page first loads. Shape depends on control/operator." + }, + "placeholder": { + "type": "string", + "description": "Optional runtime input placeholder" + }, + "helpText": { + "type": "string", + "description": "Optional runtime help text" + }, + "allowMultipleValues": { + "type": "boolean", + "description": "Whether the filter can submit multiple values. Useful with multiSelect/in/notIn.", + "default": false + }, + "clearable": { + "type": "boolean", + "description": "Whether runtime users can clear the filter.", + "default": true + }, + "initiallyExpanded": { + "type": "boolean", + "description": "Whether the runtime filter panel opens expanded.", + "default": false + } + }, + "required": ["propertyName"], + "additionalProperties": false, + "examples": [ + { "propertyName": "Title", "control": "text", "defaultOperator": "contains", "placeholder": "Search title" }, + { "propertyName": "Status", "control": "select", "defaultOperator": "equal", "clearable": true }, + { "propertyName": "StartDate", "control": "dateRange", "defaultOperator": "between" } + ] +} diff --git a/schemas/low-code/definitions/page-group-descriptor.schema.json b/schemas/low-code/definitions/page-group-descriptor.schema.json new file mode 100644 index 0000000000..c142f45500 --- /dev/null +++ b/schemas/low-code/definitions/page-group-descriptor.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-group-descriptor.schema.json", + "title": "PageGroupDescriptor", + "description": "Describes a menu group/folder that can contain pages and sub-groups.", + "markdownDescription": "AI guidance: create page groups when several pages belong to the same feature area. Pages reference groups by `group`. Nested groups reference parent groups by `parent`. Use stable kebab-case names and concise user-facing titles.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique URL-safe identifier for the group. Prefer kebab-case, for example 'crm' or 'content-studio'.", + "minLength": 1 + }, + "title": { + "type": "string", + "description": "Display title for the menu group.", + "minLength": 1 + }, + "icon": { + "type": "string", + "description": "FontAwesome icon class, for example 'fa-solid fa-folder' or 'fa-solid fa-calendar-days'." + }, + "order": { + "type": "integer", + "description": "Sort order within the parent level (lower values appear first)", + "default": 0 + }, + "parent": { + "type": "string", + "description": "Name of the parent group for nesting. Must match another pageGroups[].name. Omit for root-level groups.", + "minLength": 1 + } + }, + "required": ["name", "title"], + "additionalProperties": false, + "examples": [ + { "name": "events", "title": "Events", "icon": "fa-solid fa-calendar-days", "order": 10 }, + { "name": "event-admin", "title": "Admin", "parent": "events", "order": 20 } + ] +} diff --git a/schemas/low-code/definitions/page-permission-config.schema.json b/schemas/low-code/definitions/page-permission-config.schema.json new file mode 100644 index 0000000000..7ae1531402 --- /dev/null +++ b/schemas/low-code/definitions/page-permission-config.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-permission-config.schema.json", + "title": "PagePermissionConfig", + "description": "Permission configuration for a page's operations. Only overridden values are stored; omitted values use auto-generated defaults based on the page.", + "markdownDescription": "AI guidance: each operation can be `default`, `public`, `authenticated`, or a permission name. Omit an operation to use runtime-generated defaults. Use `public` only for anonymous pages. Use `authenticated` for pages any logged-in user can view. Use explicit permission names for controlled business operations.", + "type": "object", + "properties": { + "view": { + "type": "string", + "description": "Permission for viewing the page. Can be a specific permission name, 'default', 'public', or 'authenticated'. Omit to use auto-generated default." + }, + "create": { + "type": "string", + "description": "Permission for create operation on this page. Can be a specific permission name, 'default', 'public', or 'authenticated'. Omit to use auto-generated default." + }, + "update": { + "type": "string", + "description": "Permission for update operation on this page. Can be a specific permission name, 'default', 'public', or 'authenticated'. Omit to use auto-generated default." + }, + "delete": { + "type": "string", + "description": "Permission for delete operation on this page. Can be a specific permission name, 'default', 'public', or 'authenticated'. Omit to use auto-generated default." + } + }, + "additionalProperties": false +} diff --git a/schemas/low-code/definitions/page-type.schema.json b/schemas/low-code/definitions/page-type.schema.json new file mode 100644 index 0000000000..a3005eed50 --- /dev/null +++ b/schemas/low-code/definitions/page-type.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "page-type.schema.json", + "title": "PageType", + "description": "The runtime page renderer to use. Prefer lower-case values in descriptor JSON; PascalCase aliases are accepted for compatibility.", + "markdownDescription": "`dataGrid` renders searchable/filterable tabular CRUD for an entity. `kanban` renders grouped cards and requires `groupByProperty`. `calendar` renders entity records on a calendar and requires `calendarStartProperty`. `gallery` renders visual cards and may use `galleryImageProperty`. `form` renders a standalone create/edit form page and requires `formName`. `dashboard` renders a flat list of dashboard visualizations and requires `dashboard`.", + "type": "string", + "enum": [ + "dataGrid", + "DataGrid", + "kanban", + "Kanban", + "calendar", + "Calendar", + "form", + "Form", + "dashboard", + "Dashboard", + "gallery", + "Gallery" + ], + "enumDescriptions": [ + "Entity data grid/list page.", + "Entity data grid/list page.", + "Entity kanban board grouped by an enum/status property.", + "Entity kanban board grouped by an enum/status property.", + "Entity calendar page using date/time properties.", + "Entity calendar page using date/time properties.", + "Standalone form page bound to a form descriptor.", + "Standalone form page bound to a form descriptor.", + "Dashboard page with chart/list/number visualizations.", + "Dashboard page with chart/list/number visualizations.", + "Entity gallery/card page, optionally image-backed.", + "Entity gallery/card page, optionally image-backed." + ] +} diff --git a/schemas/low-code/definitions/permission-descriptor.schema.json b/schemas/low-code/definitions/permission-descriptor.schema.json new file mode 100644 index 0000000000..1abeb49841 --- /dev/null +++ b/schemas/low-code/definitions/permission-descriptor.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "permission-descriptor.schema.json", + "title": "PermissionDescriptor", + "description": "Defines a custom permission created through the designer. Supports parent-child hierarchy.", + "markdownDescription": "AI guidance: use stable dot-separated permission names, for example `Acme.Events.Create`. Define a parent permission for the feature and child permissions for operations. Pages and endpoints can reference these names. Permission definitions only declare permissions; roles/users must still be granted permissions through permission management.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique dot-separated permission name, for example 'Acme.Events.Create'.", + "minLength": 1 + }, + "displayName": { + "type": "string", + "description": "Human-readable display name shown in permission management UI.", + "minLength": 1 + }, + "children": { + "type": "array", + "description": "Child permissions forming a hierarchy. Use for feature -> operation grouping.", + "items": { + "$ref": "#" + } + } + }, + "required": ["name", "displayName"], + "additionalProperties": false, + "examples": [ + { + "name": "Acme.Events", + "displayName": "Events", + "children": [ + { "name": "Acme.Events.Create", "displayName": "Create events" }, + { "name": "Acme.Events.Update", "displayName": "Update events" }, + { "name": "Acme.Events.Delete", "displayName": "Delete events" } + ] + } + ] +} diff --git a/schemas/low-code/definitions/script-background-job-descriptor.schema.json b/schemas/low-code/definitions/script-background-job-descriptor.schema.json new file mode 100644 index 0000000000..9a186ad083 --- /dev/null +++ b/schemas/low-code/definitions/script-background-job-descriptor.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "script-background-job-descriptor.schema.json", + "title": "Script Background Job Descriptor", + "description": "Defines a named JavaScript background job handler.", + "markdownDescription": "AI guidance: use background jobs for asynchronous work that is explicitly enqueued by code/scripts, such as notifications, imports, or long-running recalculations. `name` is the job identifier used by enqueue calls. Keep scripts retry-safe and idempotent because background jobs may run more than once after failures.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique identifier for the background job. Scripts/enqueuers reference this name.", + "minLength": 1 + }, + "javascript": { + "type": "string", + "description": "JavaScript code to execute when the job is enqueued/run. Use context payload/job arguments where available and keep retry-safe.", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "Optional description for designer documentation and model health context." + } + }, + "required": ["name", "javascript"], + "additionalProperties": false, + "examples": [ + { + "name": "SendEventReminder", + "javascript": "context.log('Sending event reminder job.');" + } + ] +} diff --git a/schemas/low-code/definitions/script-background-worker-descriptor.schema.json b/schemas/low-code/definitions/script-background-worker-descriptor.schema.json new file mode 100644 index 0000000000..2c0ba07eb8 --- /dev/null +++ b/schemas/low-code/definitions/script-background-worker-descriptor.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "script-background-worker-descriptor.schema.json", + "title": "Script Background Worker Descriptor", + "description": "Defines a scheduled JavaScript background worker.", + "markdownDescription": "AI guidance: use background workers for recurring scheduled work. Provide either `period` in milliseconds or `cronExpression`; do not provide both unless the host explicitly chooses one. Keep scripts idempotent and short. Use workers for polling, cleanup, synchronization, or recurring summary generation.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique identifier for the background worker.", + "minLength": 1 + }, + "period": { + "type": "integer", + "description": "Execution period in milliseconds. Example: 300000 for every 5 minutes.", + "minimum": 1 + }, + "cronExpression": { + "type": ["string", "null"], + "description": "Cron expression for scheduler-backed providers. Omit it, set it to null, or leave it empty when the worker uses period-based scheduling." + }, + "javascript": { + "type": "string", + "description": "JavaScript code to execute when the worker runs. Keep idempotent; workers may overlap or retry depending on scheduler configuration.", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "Optional description for designer documentation and model health context." + } + }, + "required": ["name", "javascript"], + "anyOf": [ + { + "required": ["period"], + "not": { + "required": ["cronExpression"], + "properties": { + "cronExpression": { + "type": "string", + "minLength": 1 + } + } + } + }, + { + "required": ["cronExpression"], + "not": { "required": ["period"] }, + "properties": { + "cronExpression": { + "type": "string", + "minLength": 1 + } + } + } + ], + "additionalProperties": false, + "examples": [ + { + "name": "CleanupExpiredEvents", + "period": 3600000, + "javascript": "context.log('Running expired event cleanup.');" + } + ] +} diff --git a/schemas/low-code/definitions/script-event-handler-descriptor.schema.json b/schemas/low-code/definitions/script-event-handler-descriptor.schema.json new file mode 100644 index 0000000000..da29d7fd4d --- /dev/null +++ b/schemas/low-code/definitions/script-event-handler-descriptor.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "script-event-handler-descriptor.schema.json", + "title": "Script Event Handler Descriptor", + "description": "Defines a named JavaScript handler for a distributed event name.", + "markdownDescription": "AI guidance: use event handlers for asynchronous reactions to domain/application events. `eventName` must match the event name published by the application or another script. Keep handlers idempotent because distributed events can be retried. Scripts can use the host-provided context, including event payload/request data where available, db, current tenant/user, email, logging, event publishing, and job enqueue helpers.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "Optional schema reference used when this descriptor is stored as a model descriptor file." + }, + "name": { + "type": "string", + "description": "Unique identifier for the handler. Prefer a descriptive name such as 'NotifyWhenEventCompleted'.", + "minLength": 1 + }, + "eventName": { + "type": "string", + "description": "Distributed event name to subscribe to. Must exactly match the publisher's event name.", + "minLength": 1 + }, + "javascript": { + "type": "string", + "description": "JavaScript code to execute when the event is received. Keep idempotent; avoid assuming one-time delivery.", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "Optional description for designer documentation and model health context." + } + }, + "required": ["name", "eventName", "javascript"], + "additionalProperties": false, + "examples": [ + { + "name": "NotifyEventCompleted", + "eventName": "Acme.Events.EventCompleted", + "javascript": "context.log('Event completed handler executed.');" + } + ] +} diff --git a/schemas/low-code/definitions/validator-descriptor.schema.json b/schemas/low-code/definitions/validator-descriptor.schema.json new file mode 100644 index 0000000000..83f18a39a0 --- /dev/null +++ b/schemas/low-code/definitions/validator-descriptor.schema.json @@ -0,0 +1,96 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "validator-descriptor.schema.json", + "title": "ValidatorDescriptor", + "description": "A single backend/UI validator applied to an entity property or form field.", + "markdownDescription": "AI guidance: always set `type`. Use `required` for mandatory input, `minLength`/`maxLength`/`stringLength` for strings, `min`/`max`/`range` for numbers and comparable values, `pattern` or `regularExpression` for regex, and `email`, `phone`, `url`, or `creditCard` for common formats. Use lower-case validator names for new JSON. Add `message` only when a custom localized/user-facing message is needed.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string", + "description": "Validator type. Prefer lower-case values in descriptor JSON; PascalCase aliases are accepted for compatibility.", + "enum": [ + "required", + "Required", + "minLength", + "MinLength", + "maxLength", + "MaxLength", + "stringLength", + "StringLength", + "min", + "Min", + "minimum", + "Minimum", + "max", + "Max", + "maximum", + "Maximum", + "range", + "Range", + "pattern", + "Pattern", + "regularExpression", + "RegularExpression", + "email", + "Email", + "emailAddress", + "EmailAddress", + "phone", + "Phone", + "url", + "Url", + "creditCard", + "CreditCard" + ] + }, + "message": { + "type": "string", + "description": "Optional custom error message shown when validation fails. Omit to use the default localized message." + }, + "length": { + "type": "integer", + "description": "Length value for minLength or maxLength validators. Example: { \"type\": \"maxLength\", \"length\": 128 }.", + "minimum": 0 + }, + "minimumLength": { + "type": "integer", + "description": "Minimum length for stringLength validator.", + "minimum": 0 + }, + "maximumLength": { + "type": "integer", + "description": "Maximum length for stringLength validator.", + "minimum": 0 + }, + "value": { + "type": "number", + "description": "Generic numeric value alias for single-value validators such as min/minimum, max/maximum, minLength/maxLength, and stringLength maximumLength." + }, + "minimum": { + "type": "number", + "description": "Minimum value for min/minimum or range validators." + }, + "maximum": { + "type": "number", + "description": "Maximum value for max/maximum or range validators." + }, + "pattern": { + "type": "string", + "description": "Regular expression pattern for pattern/regularExpression validators. Store the regex pattern string only; do not include leading/trailing slashes." + }, + "allowEmptyStrings": { + "type": "boolean", + "description": "Whether required validation should allow empty strings. Usually false for user-entered text." + } + }, + "additionalProperties": true, + "examples": [ + { "type": "required" }, + { "type": "maxLength", "length": 128 }, + { "type": "range", "minimum": 0, "maximum": 100 }, + { "type": "pattern", "pattern": "^[A-Z]{3}-[0-9]{4}$", "message": "Code must match ABC-1234." }, + { "type": "email" } + ] +} diff --git a/schemas/low-code/manifest.json b/schemas/low-code/manifest.json new file mode 100644 index 0000000000..de39544885 --- /dev/null +++ b/schemas/low-code/manifest.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "name": "ABP Low-Code Model Descriptor Schemas", + "version": "10.5", + "publicBaseUrl": "https://raw.githubusercontent.com/abpframework/abp/rel-10.5/schemas/low-code", + "definitionsPath": "definitions", + "descriptorSchemas": { + "enums": "definitions/enum-descriptor.schema.json", + "entities": "definitions/entity-descriptor.schema.json", + "endpoints": "definitions/endpoint-descriptor.schema.json", + "eventHandlers": "definitions/script-event-handler-descriptor.schema.json", + "backgroundJobs": "definitions/script-background-job-descriptor.schema.json", + "backgroundWorkers": "definitions/script-background-worker-descriptor.schema.json", + "pageGroups": "definitions/page-group-descriptor.schema.json", + "pages": "definitions/page-descriptor.schema.json", + "forms": "definitions/form-descriptor.schema.json", + "permissions": "definitions/permission-descriptor.schema.json" + } +} diff --git a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip index 799c4e786b..fa9b0bd48f 100644 Binary files a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip and b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip differ diff --git a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip index b4ed66cf50..bd293c24b0 100644 Binary files a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip and b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip index 2f9e63122d..c453c1e72d 100644 Binary files a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip and b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip index 0081e55d9c..0d42237c66 100644 Binary files a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip and b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip differ diff --git a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip index 3c4fb76675..c4996b8087 100644 Binary files a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip and b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip index 4edee66c19..5e9293a300 100644 Binary files a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip and b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip differ diff --git a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip index 5bad9371c2..b3266e534b 100644 Binary files a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip and b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip differ diff --git a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip index 00f70b3c5f..453c7a6202 100644 Binary files a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip and b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip differ diff --git a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip index 656b1589da..39a13f6a6d 100644 Binary files a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip and b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip index 95589ed524..a5757cedfb 100644 Binary files a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip and b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip index ad96ce67dd..3e440fe227 100644 Binary files a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip and b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip index f665bb7e52..09b67ef9de 100644 Binary files a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip and b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip differ diff --git a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip index 746afe17b0..00c5d91b52 100644 Binary files a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip and b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip differ diff --git a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip index d7b64b08f7..2111dc86db 100644 Binary files a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip and b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip differ diff --git a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip index 0aba2c6899..49de9a94c6 100644 Binary files a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip and b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip differ diff --git a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip index ee161220e0..8f65d7123b 100644 Binary files a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip and b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip differ diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index e1a015dcf9..bf77fa3ddc 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.1", - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.identity": "~10.5.0-rc.1", - "@abp/ng.oauth": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.tenant-management": "~10.5.0-rc.1", - "@abp/ng.theme.lepton-x": "~5.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.account": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.identity": "~10.5.0-rc.3", + "@abp/ng.oauth": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.tenant-management": "~10.5.0-rc.3", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "@angular/animations": "~22.0.1", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.1", @@ -37,7 +37,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.1", + "@abp/ng.schematics": "~10.5.0-rc.3", "@angular-eslint/builder": "~21.2.0", "@angular-eslint/eslint-plugin": "~21.2.0", "@angular-eslint/eslint-plugin-template": "~21.2.0", diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json index 99885de4f9..810abfcc24 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1", - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3", + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json index 45b5ddb411..ef9e246b0c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index 48b4c4b16e..e4be98db02 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.1", - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.identity": "~10.5.0-rc.1", - "@abp/ng.oauth": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.tenant-management": "~10.5.0-rc.1", - "@abp/ng.theme.lepton-x": "~5.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.account": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.identity": "~10.5.0-rc.3", + "@abp/ng.oauth": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.tenant-management": "~10.5.0-rc.3", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "@angular/animations": "~22.0.1", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.1", @@ -37,7 +37,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.1", + "@abp/ng.schematics": "~10.5.0-rc.3", "@angular-eslint/builder": "~21.2.0", "@angular-eslint/eslint-plugin": "~21.2.0", "@angular-eslint/eslint-plugin-template": "~21.2.0", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json index 0e3b28d79b..770f6e9be2 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json index 45b5ddb411..ef9e246b0c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json index 45b5ddb411..ef9e246b0c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json index 45b5ddb411..ef9e246b0c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json index 45b5ddb411..ef9e246b0c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json index dd2024618c..fa15c8e0b3 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" } } diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 7f1fa75a68..431b1d6686 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -13,15 +13,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.1", - "@abp/ng.components": "~10.5.0-rc.1", - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.identity": "~10.5.0-rc.1", - "@abp/ng.oauth": "~10.5.0-rc.1", - "@abp/ng.setting-management": "~10.5.0-rc.1", - "@abp/ng.tenant-management": "~10.5.0-rc.1", - "@abp/ng.theme.basic": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1", + "@abp/ng.account": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.identity": "~10.5.0-rc.3", + "@abp/ng.oauth": "~10.5.0-rc.3", + "@abp/ng.setting-management": "~10.5.0-rc.3", + "@abp/ng.tenant-management": "~10.5.0-rc.3", + "@abp/ng.theme.basic": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3", "@angular/animations": "~22.0.0", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.0", @@ -37,7 +37,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.1", + "@abp/ng.schematics": "~10.5.0-rc.3", "@angular-eslint/builder": "~22.0.0", "@angular-eslint/eslint-plugin": "~22.0.0", "@angular-eslint/eslint-plugin-template": "~22.0.0", diff --git a/templates/module/angular/projects/my-project-name/package.json b/templates/module/angular/projects/my-project-name/package.json index 660b948091..db7e763e6a 100644 --- a/templates/module/angular/projects/my-project-name/package.json +++ b/templates/module/angular/projects/my-project-name/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "~21.2.0", "@angular/core": "~21.2.0", - "@abp/ng.core": "~10.5.0-rc.1", - "@abp/ng.theme.shared": "~10.5.0-rc.1" + "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.3" }, "dependencies": { "tslib": "^2.1.0" diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json index 040ae69177..6d67129b14 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json index ddd0284755..05ada78062 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1", - "@abp/aspnetcore.components.server.basictheme": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", + "@abp/aspnetcore.components.server.basictheme": "~10.5.0-rc.3" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json index 348569ddd3..f87780dd17 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json index 348569ddd3..f87780dd17 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.1" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" } }