Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions config/x2mdx/typescript-bindings/source-artifacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
"page_description": "TypeScript and JavaScript language bindings for Canton.",
"output_file": "docs-main/reference/typescript.mdx",
"entry_point": "index.d.ts",
"publish_version": "3.4.11",
"publish_version": "3.5.2",
"versions": [
"3.4.8",
"3.4.9",
"3.4.10",
"3.4.11"
"3.4.11",
"3.5.2"
]
},
{
Expand Down Expand Up @@ -44,9 +45,10 @@
"typedoc_args": [
"--skipErrorChecking"
],
"publish_version": "1.1.0",
"publish_version": "1.2.0",
"versions": [
"1.1.0"
"1.1.0",
"1.2.0"
]
}
]
Expand Down
1 change: 1 addition & 0 deletions docs-main/reference/typescript.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Generated from published `@daml/types` TypeDoc snapshots.
| `3.4.9` | - | `3` | - |
| `3.4.10` | - | - | - |
| `3.4.11` | - | - | - |
| `3.5.2` | - | - | - |

## Reference

Expand Down
363 changes: 274 additions & 89 deletions docs-main/reference/typescript/dapp-sdk.mdx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs-main/reference/typescript/wallet-sdk.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "@canton-network/wallet-sdk"
title: "Wallet SDK"
description: "TypeScript client library reference for wallet integrations."
---

Expand Down
129 changes: 129 additions & 0 deletions scripts/generated_reference_sources/typescript_bindings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Required, TypedDict
from urllib.parse import quote
from urllib.request import Request, urlopen

from generated_reference_sources.common import SourceUpdate, load_json, write_json


REPO_ROOT = Path(__file__).resolve().parents[2]
SOURCE_KEY = "typescript-bindings"
SOURCE_LABEL = "TypeScript bindings"
DEFAULT_SOURCE_CONFIG = REPO_ROOT / "config" / "x2mdx" / "typescript-bindings" / "source-artifacts.json"
DEFAULT_TIMEOUT_SECONDS = 20.0
USER_AGENT = "cf-docs-generated-reference-source-updater"


class TypeScriptPackageConfigPayload(TypedDict, total=False):
package_name: Required[str]
source: str
version_filter: str
page_title: str
page_description: str
output_file: str
entry_point: str
typedoc_args: list[str]
typedoc_version: str
publish_version: Required[str]
versions: Required[list[str]]


@dataclass(frozen=True)
class TypeScriptPackageConfig:
raw: TypeScriptPackageConfigPayload
package_name: str
publish_version: str
versions: tuple[str, ...]


@dataclass(frozen=True)
class TypeScriptBindingsSourceConfig:
raw: dict[str, object]
packages: tuple[TypeScriptPackageConfig, ...]


def parse_source_config(path: Path) -> TypeScriptBindingsSourceConfig:
raw_json = load_json(path)
packages_json = raw_json.get("packages")
if not isinstance(packages_json, list) or not packages_json:
raise ValueError(f"{path} must define a non-empty packages list")

packages: list[TypeScriptPackageConfig] = []
for index, package_json in enumerate(packages_json):
if not isinstance(package_json, dict):
raise ValueError(f"{path} packages[{index}] must be an object")
package_name = package_json.get("package_name")
publish_version = package_json.get("publish_version")
versions = package_json.get("versions")
if not isinstance(package_name, str) or not package_name:
raise ValueError(f"{path} packages[{index}] must define package_name")
if not isinstance(publish_version, str) or not publish_version:
raise ValueError(f"{path} packages[{package_name}] must define publish_version")
if not isinstance(versions, list) or not all(isinstance(version, str) and version for version in versions):
raise ValueError(f"{path} packages[{package_name}] must define a non-empty versions string list")

raw: TypeScriptPackageConfigPayload = {}
raw.update(package_json)
packages.append(
TypeScriptPackageConfig(
raw=raw,
package_name=package_name,
publish_version=publish_version,
versions=tuple(versions),
)
)
return TypeScriptBindingsSourceConfig(raw=raw_json, packages=tuple(packages))


def latest_npm_version(package_name: str, *, timeout: float = DEFAULT_TIMEOUT_SECONDS) -> str:
encoded_name = quote(package_name, safe="")
request = Request(
f"https://registry.npmjs.org/{encoded_name}",
headers={"User-Agent": USER_AGENT},
)
with urlopen(request, timeout=timeout) as response:
payload = json.loads(response.read().decode("utf-8"))
latest = payload.get("dist-tags", {}).get("latest")
if not isinstance(latest, str) or not latest:
raise ValueError(f"npm package {package_name} does not define a latest dist-tag")
return latest


def update_source(
*,
source_config_path: Path,
dry_run: bool,
) -> list[SourceUpdate]:
source_config = parse_source_config(source_config_path)
updates: list[SourceUpdate] = []
updated_packages: list[dict[str, object]] = []

for package in source_config.packages:
current_version = latest_npm_version(package.package_name)
updated_package = dict(package.raw)
if package.publish_version != current_version:
updates.append(
SourceUpdate(
source=f"{SOURCE_LABEL} {package.package_name}",
path=source_config_path,
field="publish_version",
previous=package.publish_version,
current=current_version,
)
)
versions = list(package.versions)
if current_version not in versions:
versions.append(current_version)
updated_package["publish_version"] = current_version
updated_package["versions"] = versions
updated_packages.append(updated_package)

if updates and not dry_run:
updated_config = dict(source_config.raw)
updated_config["packages"] = updated_packages
write_json(source_config_path, updated_config)
return updates
35 changes: 35 additions & 0 deletions scripts/summarize_version_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ def source_config_changes(before_path: Path, after_path: Path, *, label: str) ->
return changes


def package_source_config_changes(before_path: Path, after_path: Path, *, label: str) -> list[str]:
before = load_json(before_path)
after = load_json(after_path)
before_packages = {
package["package_name"]: package
for package in object_items(before.get("packages"))
if isinstance(package.get("package_name"), str)
}
changes: list[str] = []
for package in object_items(after.get("packages")):
package_name = package.get("package_name")
if not isinstance(package_name, str):
continue
before_package = before_packages.get(package_name)
if before_package is None:
continue
before_version = before_package.get("publish_version")
after_version = package.get("publish_version")
if before_version != after_version:
changes.append(
f"- {label} {package_name} publish_version: "
f"{format_value(before_version)} -> {format_value(after_version)}"
)
return changes


def print_changes(changes: list[str]) -> None:
if changes:
print("\n".join(changes))
Expand All @@ -160,6 +186,13 @@ def parse_args() -> argparse.Namespace:
source_config.add_argument("before", type=Path)
source_config.add_argument("after", type=Path)
source_config.add_argument("--label", required=True)
package_source_config = subparsers.add_parser(
"package-source-config",
help="Summarize package-based generated-reference source config changes.",
)
package_source_config.add_argument("before", type=Path)
package_source_config.add_argument("after", type=Path)
package_source_config.add_argument("--label", required=True)
return parser.parse_args()


Expand All @@ -169,6 +202,8 @@ def main() -> int:
print_changes(dashboard_changes(args.before, args.after))
elif args.command == "source-config":
print_changes(source_config_changes(args.before, args.after, label=args.label))
elif args.command == "package-source-config":
print_changes(package_source_config_changes(args.before, args.after, label=args.label))
else:
raise AssertionError(f"Unhandled command: {args.command}")
return 0
Expand Down
35 changes: 35 additions & 0 deletions scripts/update_generated_reference_prs.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,33 @@ class UpdateTarget:
"git diff --check",
),
),
UpdateTarget(
key="typescript-bindings",
title="Update TypeScript bindings reference",
branch="generated-references/typescript-bindings/update",
description=(
"Updates the TypeScript bindings source pins to the latest stable npm "
"releases and regenerates the checked-in TypeScript bindings reference pages."
),
generate_commands=(
("nix-shell", "--run", "npm run update:generated-reference-sources -- --source typescript-bindings"),
("nix-shell", "--run", "npm run generate:typescript-bindings-reference"),
),
paths=(
"config/x2mdx/typescript-bindings/source-artifacts.json",
"docs-main/docs.json",
"docs-main/reference/typescript.mdx",
"docs-main/reference/typescript",
),
summary_kind="package-source-config",
summary_path="config/x2mdx/typescript-bindings/source-artifacts.json",
summary_label="TypeScript bindings",
validation=(
"npm run update:generated-reference-sources -- --source typescript-bindings",
"npm run generate:typescript-bindings-reference",
"git diff --check",
),
),
)


Expand Down Expand Up @@ -124,6 +151,14 @@ def summarize_target_changes(target: UpdateTarget, before_path: Path) -> list[st
after_path,
label=target.summary_label,
)
if target.summary_kind == "package-source-config":
if target.summary_label is None:
raise ValueError(f"Update target {target.key} must define summary_label")
return summarize_version_changes.package_source_config_changes(
before_path,
after_path,
label=target.summary_label,
)
raise ValueError(f"Unknown summary kind for {target.key}: {target.summary_kind}")


Expand Down
21 changes: 19 additions & 2 deletions scripts/update_generated_reference_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
import sys
from pathlib import Path

from generated_reference_sources import splice_openapi, wallet_gateway_openrpc
from generated_reference_sources import splice_openapi, typescript_bindings, wallet_gateway_openrpc
from generated_reference_sources.common import SourceUpdate


SOURCE_SPLICE_OPENAPI = splice_openapi.SOURCE_KEY
SOURCE_WALLET_GATEWAY_OPENRPC = wallet_gateway_openrpc.SOURCE_KEY
ALL_SOURCES = (SOURCE_SPLICE_OPENAPI, SOURCE_WALLET_GATEWAY_OPENRPC)
SOURCE_TYPESCRIPT_BINDINGS = typescript_bindings.SOURCE_KEY
ALL_SOURCES = (SOURCE_SPLICE_OPENAPI, SOURCE_WALLET_GATEWAY_OPENRPC, SOURCE_TYPESCRIPT_BINDINGS)


def parse_args() -> argparse.Namespace:
Expand All @@ -34,6 +35,15 @@ def parse_args() -> argparse.Namespace:
f"Default: {wallet_gateway_openrpc.DEFAULT_SOURCE_CONFIG}"
),
)
parser.add_argument(
"--typescript-bindings-source-config",
type=Path,
default=typescript_bindings.DEFAULT_SOURCE_CONFIG,
help=(
"TypeScript bindings source-artifacts config. "
f"Default: {typescript_bindings.DEFAULT_SOURCE_CONFIG}"
),
)
parser.add_argument(
"--source",
action="append",
Expand Down Expand Up @@ -79,6 +89,13 @@ def main() -> int:
)
if update is not None:
updates.append(update)
if SOURCE_TYPESCRIPT_BINDINGS in sources:
updates.extend(
typescript_bindings.update_source(
source_config_path=args.typescript_bindings_source_config.resolve(),
dry_run=args.dry_run or args.check,
)
)

if not updates:
print("Generated reference source pins are up to date.")
Expand Down
29 changes: 29 additions & 0 deletions tests/test_summarize_version_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,32 @@ def test_source_config_changes_summarizes_publish_version(tmp_path: Path) -> Non
assert module.source_config_changes(before, after, label="Wallet Gateway OpenRPC") == [
"- Wallet Gateway OpenRPC publish_version: 0.25.0 -> 1.4.0"
]


def test_package_source_config_changes_summarizes_package_publish_versions(tmp_path: Path) -> None:
module = load_script_module()
before = tmp_path / "before.json"
after = tmp_path / "after.json"
write_json(
before,
{
"packages": [
{"package_name": "@daml/types", "publish_version": "3.4.11"},
{"package_name": "@canton-network/dapp-sdk", "publish_version": "1.1.0"},
]
},
)
write_json(
after,
{
"packages": [
{"package_name": "@daml/types", "publish_version": "3.5.2"},
{"package_name": "@canton-network/dapp-sdk", "publish_version": "1.2.0"},
]
},
)

assert module.package_source_config_changes(before, after, label="TypeScript bindings") == [
"- TypeScript bindings @daml/types publish_version: 3.4.11 -> 3.5.2",
"- TypeScript bindings @canton-network/dapp-sdk publish_version: 1.1.0 -> 1.2.0",
]
1 change: 1 addition & 0 deletions tests/test_update_generated_reference_prs.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def test_generated_clean_paths_include_target_paths_and_internal_output() -> Non

assert ".internal" in clean_paths
assert "docs-main/reference/wallet-gateway-json-rpc" in clean_paths
assert "docs-main/reference/typescript" in clean_paths
assert "docs-main/snippets/generated/version-dashboard-data.mdx" in clean_paths


Expand Down
Loading