Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLeitwolf112026-10-29 12:42:13 +0300
committerLeitwolf112026-10-29 12:42:13 +0300
commit285e1024da64234b30b0fa425ffd6bf348119db4 (patch)
tree9801dc805a397052264cc5bc0a6c56cf870b179a
parent3d1c6d52b830d201a68b0440a8a81c13b74c8b92 (diff)
downloadprogmaweb-285e1024da64234b30b0fa425ffd6bf348119db4.tar.gz
Document the ViGet API in full and take zod 4.6.7 for eslint
The documentation check of the backend found the private members of ViGet/ without a summary and no page for its namespace: - Summaries for every field and helper method of the catalog, the descriptor, the properties reader, publishing and the endpoints. - docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md describes the namespace. - The two regular expressions of TazelPluginDescriptor are excluded from the reference like AccountNamePolicy.CanonicalPattern: the generator replaces their comment with its own. The dependency audit found GHSA for zod < 4.6.6, which can exhaust memory while validating oversized constrained collections. Only eslint-plugin-react-hooks reaches it, and it accepts any zod 4, so an override takes 4.6.7 until the plugin resolves a fixed release itself. pnpm audit --audit-level high finds nothing.
-rw-r--r--apps/api/ViGet/JavaProperties.cs6
-rw-r--r--apps/api/ViGet/PostgresTazelPluginCatalog.cs19
-rw-r--r--apps/api/ViGet/TazelPluginCatalog.cs2
-rw-r--r--apps/api/ViGet/TazelPluginDescriptor.cs4
-rw-r--r--apps/api/ViGet/TazelPluginPublishing.cs3
-rw-r--r--apps/api/ViGet/ViGetEndpoints.cs6
-rw-r--r--apps/api/ViGet/ViGetRegistration.cs1
-rw-r--r--docs/api/filterConfig.yml3
-rw-r--r--docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md11
-rw-r--r--pnpm-lock.yaml17
-rw-r--r--pnpm-workspace.yaml4
11 files changed, 68 insertions, 8 deletions
diff --git a/apps/api/ViGet/JavaProperties.cs b/apps/api/ViGet/JavaProperties.cs
index e55991e..1b42f20 100644
--- a/apps/api/ViGet/JavaProperties.cs
+++ b/apps/api/ViGet/JavaProperties.cs
@@ -93,10 +93,16 @@ internal static class JavaProperties
}
}
+ /// <summary>Whether a character is white space of the format: space, tab or form feed.</summary>
private static bool IsBlank(char character) => character is ' ' or '\t' or '\f';
+ /// <summary>Whether an unescaped character ends a key.</summary>
private static bool IsKeyEnd(char character) => character is '=' or ':' || IsBlank(character);
+ /// <summary>Resolves the escapes of a key or a value.</summary>
+ /// <param name="text">The text as the file writes it.</param>
+ /// <returns>The text it stands for.</returns>
+ /// <exception cref="FormatException">A <c>\u</c> escape is not followed by four hexadecimal digits.</exception>
private static string Unescape(string text)
{
StringBuilder result = new(text.Length);
diff --git a/apps/api/ViGet/PostgresTazelPluginCatalog.cs b/apps/api/ViGet/PostgresTazelPluginCatalog.cs
index 07fa86f..9ae20b0 100644
--- a/apps/api/ViGet/PostgresTazelPluginCatalog.cs
+++ b/apps/api/ViGet/PostgresTazelPluginCatalog.cs
@@ -15,6 +15,7 @@ internal static class ViGetSchema
/// <summary>Key of the advisory lock that lets one process at a time change these tables.</summary>
private const long MigrationLockKey = 0x5669476574;
+ /// <summary>The steps, in order. The version of a step is its position, counted from one.</summary>
private static readonly string[] Steps =
[
// 1: Tazel plugins and their versions.
@@ -101,6 +102,12 @@ internal static class ViGetSchema
return taken;
}
+ /// <summary>Runs statements that return nothing.</summary>
+ /// <param name="connection">The connection to run them on.</param>
+ /// <param name="transaction">The transaction they belong to.</param>
+ /// <param name="sql">The statements.</param>
+ /// <param name="cancellationToken">Cancels the operation.</param>
+ /// <returns>A task that completes when the statements have run.</returns>
private static async Task ExecuteAsync(
NpgsqlConnection connection,
NpgsqlTransaction transaction,
@@ -116,6 +123,7 @@ internal static class ViGetSchema
/// <param name="dataSource">Connections to the database of the accounts.</param>
internal sealed class PostgresTazelPluginCatalog(NpgsqlDataSource dataSource) : ITazelPluginCatalog
{
+ /// <summary>The columns of a plugin row, in the order <see cref="ReadPlugin"/> reads them.</summary>
private const string PluginColumns = "publisher, name, description, authors, license, homepage, repository";
/// <inheritdoc/>
@@ -265,6 +273,10 @@ internal sealed class PostgresTazelPluginCatalog(NpgsqlDataSource dataSource) :
return await command.ExecuteNonQueryAsync(cancellationToken) == 1;
}
+ /// <summary>A plugin from a row of <see cref="PluginColumns"/>.</summary>
+ /// <param name="reader">A reader on the row.</param>
+ /// <param name="versions">The versions of the plugin.</param>
+ /// <returns>The plugin.</returns>
private static TazelPlugin ReadPlugin(NpgsqlDataReader reader, IReadOnlyList<TazelPluginVersion> versions) => new(
reader.GetString(0),
reader.GetString(1),
@@ -275,6 +287,13 @@ internal sealed class PostgresTazelPluginCatalog(NpgsqlDataSource dataSource) :
reader.IsDBNull(6) ? null : reader.GetString(6),
versions);
+ /// <summary>The versions of a plugin, newest publication first.</summary>
+ /// <param name="connection">The connection to read on.</param>
+ /// <param name="transaction">The transaction to read in, if any.</param>
+ /// <param name="publisher">The publisher of the plugin.</param>
+ /// <param name="name">The name of the plugin.</param>
+ /// <param name="cancellationToken">Cancels the operation.</param>
+ /// <returns>The versions.</returns>
private static async Task<IReadOnlyList<TazelPluginVersion>> VersionsAsync(
NpgsqlConnection connection,
NpgsqlTransaction? transaction,
diff --git a/apps/api/ViGet/TazelPluginCatalog.cs b/apps/api/ViGet/TazelPluginCatalog.cs
index e6fa2e9..ea7ff7a 100644
--- a/apps/api/ViGet/TazelPluginCatalog.cs
+++ b/apps/api/ViGet/TazelPluginCatalog.cs
@@ -85,7 +85,9 @@ internal interface ITazelPluginCatalog
/// <summary>A catalog that lives as long as the process, for development and tests.</summary>
internal sealed class InMemoryTazelPluginCatalog : ITazelPluginCatalog
{
+ /// <summary>Guards <see cref="plugins"/>.</summary>
private readonly Lock gate = new();
+ /// <summary>The plugins by their exact publisher and name.</summary>
private readonly Dictionary<(string Publisher, string Name), TazelPlugin> plugins = [];
/// <inheritdoc/>
diff --git a/apps/api/ViGet/TazelPluginDescriptor.cs b/apps/api/ViGet/TazelPluginDescriptor.cs
index eddd541..7495e46 100644
--- a/apps/api/ViGet/TazelPluginDescriptor.cs
+++ b/apps/api/ViGet/TazelPluginDescriptor.cs
@@ -126,9 +126,13 @@ internal sealed partial record TazelPluginDescriptor(
return null;
}
+ /// <summary>One identifier: a letter or <c>_</c>, then letters, digits or <c>_</c>.</summary>
+ /// <returns>The compiled expression.</returns>
[GeneratedRegex("^[A-Za-z_][A-Za-z0-9_]*$")]
private static partial Regex IdentifierPattern();
+ /// <summary>An exact semantic version without leading zeros or build metadata, as Tazel accepts it.</summary>
+ /// <returns>The compiled expression.</returns>
[GeneratedRegex(@"^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$")]
private static partial Regex VersionPattern();
}
diff --git a/apps/api/ViGet/TazelPluginPublishing.cs b/apps/api/ViGet/TazelPluginPublishing.cs
index 625e55d..02c1d6b 100644
--- a/apps/api/ViGet/TazelPluginPublishing.cs
+++ b/apps/api/ViGet/TazelPluginPublishing.cs
@@ -144,5 +144,8 @@ internal sealed class TazelPluginPublishing(
return true;
}
+ /// <summary>The result of a refused publication.</summary>
+ /// <param name="error">Why it was refused.</param>
+ /// <returns>The result.</returns>
private static PublishResult Refused(string error) => new(null, error);
}
diff --git a/apps/api/ViGet/ViGetEndpoints.cs b/apps/api/ViGet/ViGetEndpoints.cs
index 4e2337f..00f6dc4 100644
--- a/apps/api/ViGet/ViGetEndpoints.cs
+++ b/apps/api/ViGet/ViGetEndpoints.cs
@@ -96,6 +96,12 @@ internal static class ViGetEndpoints
});
}
+ /// <summary>A plugin of the catalog, or <see langword="null"/> when a route value is not an identifier.</summary>
+ /// <param name="catalog">The catalog.</param>
+ /// <param name="publisher">The publisher from the route.</param>
+ /// <param name="name">The name from the route.</param>
+ /// <param name="cancellationToken">Cancels the operation.</param>
+ /// <returns>The plugin, if there is one.</returns>
private static ValueTask<TazelPlugin?> FindAsync(
ITazelPluginCatalog catalog,
string publisher,
diff --git a/apps/api/ViGet/ViGetRegistration.cs b/apps/api/ViGet/ViGetRegistration.cs
index bb2bf6f..42da18e 100644
--- a/apps/api/ViGet/ViGetRegistration.cs
+++ b/apps/api/ViGet/ViGetRegistration.cs
@@ -40,6 +40,7 @@ internal sealed class ViGetSchemaInitializer(
NpgsqlDataSource dataSource,
ILogger<ViGetSchemaInitializer> logger) : IHostedService
{
+ /// <summary>Writes the schema version and the number of steps taken at this start.</summary>
private static readonly Action<ILogger, int, int, Exception?> SchemaReport = LoggerMessage.Define<int, int>(
LogLevel.Information,
new EventId(1, "ViGetSchema"),
diff --git a/docs/api/filterConfig.yml b/docs/api/filterConfig.yml
index 5b7b327..ec7ce4f 100644
--- a/docs/api/filterConfig.yml
+++ b/docs/api/filterConfig.yml
@@ -12,3 +12,6 @@ apiRules:
- exclude:
uidRegex: ^Progmasoft\.Progmaweb\.Api\.Accounts\.AccountNamePolicy\.CanonicalPattern$
type: Method
+ - exclude:
+ uidRegex: ^Progmasoft\.Progmaweb\.Api\.ViGet\.TazelPluginDescriptor\.(IdentifierPattern|VersionPattern)$
+ type: Method
diff --git a/docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md b/docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md
new file mode 100644
index 0000000..bb7e94e
--- /dev/null
+++ b/docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md
@@ -0,0 +1,11 @@
+---
+uid: Progmasoft.Progmaweb.Api.ViGet
+summary: *content
+---
+
+<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> -->
+<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -->
+
+ViGet's catalog of Tazel plugins: the plugins and their versions in the account database, the descriptor of a plugin
+JAR, the rules of publishing, the read endpoints under `/api/v1/viget/dslplugins/tazel` with the index Tazel reads, and
+the `viget` commands that publish and yank plugins on the server while publishing is open to Progmasoft alone.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e2d5c89..6d6f591 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,6 +6,7 @@ settings:
overrides:
brace-expansion@>=5.0.0 <5.0.12: 5.0.12
+ zod@<4.6.6: 4.6.7
importers:
@@ -1945,10 +1946,10 @@ packages:
resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
- zod: ^3.25.0 || ^4.0.0
+ zod: 4.6.7
- zod@4.4.3:
- resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+ zod@4.6.7:
+ resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
snapshots:
@@ -3001,8 +3002,8 @@ snapshots:
'@babel/parser': 7.29.8
eslint: 9.39.2(supports-color@7.2.0)
hermes-parser: 0.25.1
- zod: 4.4.3
- zod-validation-error: 4.0.2(zod@4.4.3)
+ zod: 4.6.7
+ zod-validation-error: 4.0.2(zod@4.6.7)
transitivePeerDependencies:
- supports-color
@@ -4106,8 +4107,8 @@ snapshots:
yocto-queue@0.1.0: {}
- zod-validation-error@4.0.2(zod@4.4.3):
+ zod-validation-error@4.0.2(zod@4.6.7):
dependencies:
- zod: 4.4.3
+ zod: 4.6.7
- zod@4.4.3: {}
+ zod@4.6.7: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 7b3dd1b..ac1f58f 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -12,6 +12,10 @@ overrides:
# published denial-of-service advisories fixed by 5.0.12. Remove this once
# minimatch depends on a fixed release itself.
"brace-expansion@>=5.0.0 <5.0.12": 5.0.12
+ # eslint-plugin-react-hooks resolves zod 4.4.3, which can exhaust memory while
+ # validating oversized constrained collections (fixed in 4.6.6). The plugin
+ # accepts any zod 4; remove this once it resolves a fixed release itself.
+ "zod@<4.6.6": 4.6.7
auditConfig:
ignoreGhsas:
# braces <= 3.0.3: stack exhaustion on deeply nested brace patterns. No