diff --git a/.bazelignore b/.bazelignore deleted file mode 100644 index af73bfcff7c4..000000000000 --- a/.bazelignore +++ /dev/null @@ -1,77 +0,0 @@ -# Bazel does not yet support wildcards or other .gitignore semantics for -# .bazelignore. Two issues for this feature request are outstanding: -# https://github.com/bazelbuild/bazel/issues/7093 -# https://github.com/bazelbuild/bazel/issues/8106 -.git -node_modules -dist/ - -# All integration test node_modules folders -integration/animations/node_modules -integration/animations-async/node_modules -integration/cli-elements-universal/node_modules -integration/cli-hello-world/node_modules -integration/cli-hello-world-ivy-i18n/node_modules -integration/cli-hello-world-lazy/node_modules -integration/cli-hello-world-mocha/node_modules -integration/cli-signal-inputs/node_modules -integration/defer/node_modules -integration/dynamic-compiler/node_modules -integration/forms/node_modules -integration/injectable-def/node_modules -integration/ivy-i18n/node_modules -integration/ng_elements/node_modules -integration/ng_update/node_modules -integration/ng_update_migrations/node_modules -integration/ng-add-localize/node_modules -integration/no_ts_linker/node_modules -integration/nodenext_resolution/node_modules -integration/platform-server/node_modules -integration/platform-server-zoneless/node_modules -integration/platform-server-hydration/node_modules -integration/service-worker-schema/node_modules -integration/side-effects/node_modules -integration/standalone-bootstrap/node_modules -integration/terser/node_modules -integration/trusted-types/node_modules -integration/typings_test_rxjs7/node_modules -integration/legacy-animations/node_modules -integration/legacy-animations-async/node_modules -integration/typings_test_ts59/node_modules -modules/ssr-benchmarks/node_modules -vscode-ng-language-service/integration/project/dist/ - -# For rules_js -adev/node_modules -adev/shared-docs/node_modules -adev/shared-docs/pipeline/api-gen/node_modules -dev-app/node_modules -modules/node_modules -integration/node_modules -packages/animations/node_modules -packages/common/node_modules -packages/localize/node_modules -packages/compiler-cli/node_modules -packages/compiler-cli/linker/babel/test/node_modules -packages/compiler/node_modules -packages/core/node_modules -packages/core/test/bundling/node_modules -packages/elements/node_modules -packages/forms/node_modules -packages/language-service/node_modules -packages/platform-browser/node_modules -packages/platform-server/node_modules -packages/platform-browser-dynamic/node_modules -packages/router/node_modules -packages/zone.js/node_modules -packages/zone.js/test/typings/node_modules -packages/upgrade/node_modules -packages/benchpress/node_modules -packages/service-worker/node_modules -packages/zone.js/test/typings/node_modules -packages/zone.js/node_modules -tools/bazel/rules_angular_store/node_modules -vscode-ng-language-service/node_modules -vscode-ng-language-service/server/node_modules -vscode-ng-language-service/integration/pre_standalone_project/node_modules -vscode-ng-language-service/integration/project/node_modules diff --git a/.bazelrc b/.bazelrc index 033be894fe4c..415b7ec60058 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,6 @@ +# Disable NG CLI TTY mode +build --action_env=NG_FORCE_TTY=false + # Enable debugging tests with --config=debug test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results --strategy=TestRunner=standalone @@ -143,7 +146,7 @@ build:trusted-build --remote_upload_local_results=true # Ensure that tags like "no-remote-exec" get propagated to actions created by rules, # even if the rule implementation does not explicitly pass them to the execution requirements. # https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation -common --experimental_allow_tags_propagation +common --incompatible_allow_tags_propagation # Disable network access in the sandbox by default. To enable network access # for a particular target, use: diff --git a/.bazelversion b/.bazelversion index 5942a0d3a0e7..6d2890793d47 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.7.1 +8.5.0 diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 0f36d35c7c08..88daaab3ff7f 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -3,11 +3,11 @@ This folder contains configuration files that can be used to opt into working on this repository in a [Docker container](https://www.docker.com/resources/what-container) via [VSCode](https://code.visualstudio.com/)'s Remote Development feature (see below). Info on remote development and developing inside a container with VSCode: + - [VSCode: Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) - [VSCode: Developing inside a Container](https://code.visualstudio.com/docs/remote/containers) - [VSCode: Remote Development FAQ](https://code.visualstudio.com/docs/remote/faq) - ## Usage _Prerequisite: [Install Docker](https://docs.docker.com/install) on your local environment._ @@ -15,6 +15,7 @@ _Prerequisite: [Install Docker](https://docs.docker.com/install) on your local e To get started, read and follow the instructions in [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers). The [.devcontainer/](.) directory contains pre-configured `devcontainer.json` and `Dockerfile` files, which you can use to set up remote development with a docker container. In a nutshell, you need to: + - Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension. - Copy [recommended-Dockerfile](./recommended-Dockerfile) to `Dockerfile` (and optionally tweak to suit your needs). - Copy [recommended-devcontainer.json](./recommended-devcontainer.json) to `devcontainer.json` (and optionally tweak to suit your needs). @@ -23,7 +24,6 @@ In a nutshell, you need to: The `.devcontainer/devcontainer.json` and `.devcontainer/Dockerfile` files are ignored by git, so you can have your own local versions. We may occasionally update the template files ([recommended-devcontainer.json](./recommended-devcontainer.json), [recommended-Dockerfile](./recommended-Dockerfile)), in which case you will need to manually update your local copies (if desired). - ## Updating `recommended-devcontainer.json` and `recommended-Dockerfile` You can update and commit the recommended config files (which people use as basis for their local configs), if you find that something is broken, out-of-date or can be improved. diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 0384be407b6f..8283170d78e5 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -19,6 +19,8 @@ cc34e5fb072e7903e01463953a19d18503a39347 f9781f9804f1e7c84df356d9aeb267730a7fc499 dc62446ef7f2898a20d3be1196ecaf2a92a1077d 711cb416260ca11ff158fc39b372efa2cf022d36 +af77b89e2abb67bba201a53d500874ad572b8384 +6270bba056cfaadb3fe0b2bf3b17036bdc28b90e # commits that switch to relative imports 3d2263cb1f208e561692e41c5a17196c3c57877f ae047c59c0041e0a43cec020520d40ccd981b444 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0c063fb46919..a1e4a6b7b66f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,13 @@ ## PR Checklist + Please check if your PR fulfills the following requirements: - [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) - ## PR Type + What kind of change does this PR introduce? @@ -21,23 +22,19 @@ What kind of change does this PR introduce? - [ ] angular.dev application / infrastructure changes - [ ] Other... Please describe: - ## What is the current behavior? + Issue Number: N/A - ## What is the new behavior? - ## Does this PR introduce a breaking change? - [ ] Yes - [ ] No - - ## Other information diff --git a/.github/actions/deploy-docs-site/lib/main.mts b/.github/actions/deploy-docs-site/lib/main.mts index a8677de254bd..07e4203256f0 100644 --- a/.github/actions/deploy-docs-site/lib/main.mts +++ b/.github/actions/deploy-docs-site/lib/main.mts @@ -3,7 +3,7 @@ import {context} from '@actions/github'; import {deployToFirebase, setupRedirect} from './deploy.mjs'; import {getDeployments} from './deployments.mjs'; import {generateSitemap} from './sitemap.mjs'; -import {AuthenticatedGitClient, GithubConfig, setConfig} from '@angular/ng-dev'; +import {assertValidGithubConfig, AuthenticatedGitClient, getConfig} from '@angular/ng-dev'; import {githubReleaseTrainReadToken} from './credential.mjs'; import {spawnSync} from 'child_process'; import {cp, mkdtemp} from 'fs/promises'; @@ -13,13 +13,7 @@ import {join} from 'path'; const refMatcher = /refs\/heads\/(.*)/; async function deployDocs() { - setConfig({ - github: { - mainBranchName: 'main', - name: 'angular', - owner: 'angular', - } as GithubConfig, - }); + getConfig([assertValidGithubConfig]); AuthenticatedGitClient.configure(githubReleaseTrainReadToken); diff --git a/.github/actions/deploy-docs-site/main.js b/.github/actions/deploy-docs-site/main.js index b86bdb0a23e3..d0a7a46b2b12 100644 --- a/.github/actions/deploy-docs-site/main.js +++ b/.github/actions/deploy-docs-site/main.js @@ -38,7 +38,8 @@ var require_utils = __commonJS({ ""(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); - exports.toCommandProperties = exports.toCommandValue = void 0; + exports.toCommandValue = toCommandValue; + exports.toCommandProperties = toCommandProperties; function toCommandValue(input) { if (input === null || input === void 0) { return ""; @@ -47,7 +48,6 @@ var require_utils = __commonJS({ } return JSON.stringify(input); } - exports.toCommandValue = toCommandValue; function toCommandProperties(annotationProperties) { if (!Object.keys(annotationProperties).length) { return {}; @@ -61,7 +61,6 @@ var require_utils = __commonJS({ endColumn: annotationProperties.endColumn }; } - exports.toCommandProperties = toCommandProperties; } }); @@ -89,31 +88,42 @@ var require_command = __commonJS({ } : function(o, v) { o["default"] = v; }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); Object.defineProperty(exports, "__esModule", { value: true }); - exports.issue = exports.issueCommand = void 0; + exports.issueCommand = issueCommand; + exports.issue = issue; var os2 = __importStar(__require("os")); var utils_1 = require_utils(); function issueCommand(command2, properties, message) { const cmd = new Command(command2, properties, message); process.stdout.write(cmd.toString() + os2.EOL); } - exports.issueCommand = issueCommand; function issue(name, message = "") { issueCommand(name, {}, message); } - exports.issue = issue; var CMD_STRING = "::"; var Command = class { constructor(command2, properties, message) { @@ -180,20 +190,33 @@ var require_file_command = __commonJS({ } : function(o, v) { o["default"] = v; }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); Object.defineProperty(exports, "__esModule", { value: true }); - exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; + exports.issueFileCommand = issueFileCommand; + exports.prepareKeyValueMessage = prepareKeyValueMessage; var crypto = __importStar(__require("crypto")); var fs = __importStar(__require("fs")); var os2 = __importStar(__require("os")); @@ -210,7 +233,6 @@ var require_file_command = __commonJS({ encoding: "utf8" }); } - exports.issueFileCommand = issueFileCommand; function prepareKeyValueMessage(key, value) { const delimiter = `ghadelimiter_${crypto.randomUUID()}`; const convertedValue = (0, utils_1.toCommandValue)(value); @@ -222,7 +244,6 @@ var require_file_command = __commonJS({ } return `${key}<<${delimiter}${os2.EOL}${convertedValue}${os2.EOL}${delimiter}`; } - exports.prepareKeyValueMessage = prepareKeyValueMessage; } }); @@ -231,7 +252,8 @@ var require_proxy = __commonJS({ ""(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); - exports.checkBypass = exports.getProxyUrl = void 0; + exports.getProxyUrl = getProxyUrl; + exports.checkBypass = checkBypass; function getProxyUrl(reqUrl) { const usingSsl = reqUrl.protocol === "https:"; if (checkBypass(reqUrl)) { @@ -255,7 +277,6 @@ var require_proxy = __commonJS({ return void 0; } } - exports.getProxyUrl = getProxyUrl; function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; @@ -287,7 +308,6 @@ var require_proxy = __commonJS({ } return false; } - exports.checkBypass = checkBypass; function isLoopbackAddress(host) { const hostLower = host.toLowerCase(); return hostLower === "localhost" || hostLower.startsWith("127.") || hostLower.startsWith("[::1]") || hostLower.startsWith("[0:0:0:0:0:0:0:1]"); @@ -569,18 +589,30 @@ var require_lib = __commonJS({ } : function(o, v) { o["default"] = v; }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve5) { @@ -609,7 +641,9 @@ var require_lib = __commonJS({ }); }; Object.defineProperty(exports, "__esModule", { value: true }); - exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; + exports.HttpClient = exports.HttpClientResponse = exports.HttpClientError = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; + exports.getProxyUrl = getProxyUrl; + exports.isHttps = isHttps; var http = __importStar(__require("http")); var https = __importStar(__require("https")); var pm = __importStar(require_proxy()); @@ -658,7 +692,6 @@ var require_lib = __commonJS({ const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); return proxyUrl ? proxyUrl.href : ""; } - exports.getProxyUrl = getProxyUrl; var HttpRedirectCodes = [ HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, @@ -719,7 +752,6 @@ var require_lib = __commonJS({ const parsedUrl = new URL(requestUrl); return parsedUrl.protocol === "https:"; } - exports.isHttps = isHttps; var HttpClient = class { constructor(userAgent2, handlers, requestOptions) { this._ignoreSslError = false; @@ -802,36 +834,36 @@ var require_lib = __commonJS({ * Gets a typed object from an endpoint * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise */ - getJson(requestUrl, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { + getJson(requestUrl_1) { + return __awaiter(this, arguments, void 0, function* (requestUrl, additionalHeaders = {}) { additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); const res = yield this.get(requestUrl, additionalHeaders); return this._processResponse(res, this.requestOptions); }); } - postJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { + postJson(requestUrl_1, obj_1) { + return __awaiter(this, arguments, void 0, function* (requestUrl, obj, additionalHeaders = {}) { const data = JSON.stringify(obj, null, 2); additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultContentTypeHeader(additionalHeaders, MediaTypes.ApplicationJson); const res = yield this.post(requestUrl, data, additionalHeaders); return this._processResponse(res, this.requestOptions); }); } - putJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { + putJson(requestUrl_1, obj_1) { + return __awaiter(this, arguments, void 0, function* (requestUrl, obj, additionalHeaders = {}) { const data = JSON.stringify(obj, null, 2); additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultContentTypeHeader(additionalHeaders, MediaTypes.ApplicationJson); const res = yield this.put(requestUrl, data, additionalHeaders); return this._processResponse(res, this.requestOptions); }); } - patchJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { + patchJson(requestUrl_1, obj_1) { + return __awaiter(this, arguments, void 0, function* (requestUrl, obj, additionalHeaders = {}) { const data = JSON.stringify(obj, null, 2); additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultContentTypeHeader(additionalHeaders, MediaTypes.ApplicationJson); const res = yield this.patch(requestUrl, data, additionalHeaders); return this._processResponse(res, this.requestOptions); }); @@ -1027,12 +1059,65 @@ var require_lib = __commonJS({ } return lowercaseKeys2(headers || {}); } + /** + * Gets an existing header value or returns a default. + * Handles converting number header values to strings since HTTP headers must be strings. + * Note: This returns string | string[] since some headers can have multiple values. + * For headers that must always be a single string (like Content-Type), use the + * specialized _getExistingOrDefaultContentTypeHeader method instead. + */ _getExistingOrDefaultHeader(additionalHeaders, header, _default) { let clientHeader; if (this.requestOptions && this.requestOptions.headers) { - clientHeader = lowercaseKeys2(this.requestOptions.headers)[header]; + const headerValue = lowercaseKeys2(this.requestOptions.headers)[header]; + if (headerValue) { + clientHeader = typeof headerValue === "number" ? headerValue.toString() : headerValue; + } } - return additionalHeaders[header] || clientHeader || _default; + const additionalValue = additionalHeaders[header]; + if (additionalValue !== void 0) { + return typeof additionalValue === "number" ? additionalValue.toString() : additionalValue; + } + if (clientHeader !== void 0) { + return clientHeader; + } + return _default; + } + /** + * Specialized version of _getExistingOrDefaultHeader for Content-Type header. + * Always returns a single string (not an array) since Content-Type should be a single value. + * Converts arrays to comma-separated strings and numbers to strings to ensure type safety. + * This was split from _getExistingOrDefaultHeader to provide stricter typing for callers + * that assign the result to places expecting a string (e.g., additionalHeaders[Headers.ContentType]). + */ + _getExistingOrDefaultContentTypeHeader(additionalHeaders, _default) { + let clientHeader; + if (this.requestOptions && this.requestOptions.headers) { + const headerValue = lowercaseKeys2(this.requestOptions.headers)[Headers.ContentType]; + if (headerValue) { + if (typeof headerValue === "number") { + clientHeader = String(headerValue); + } else if (Array.isArray(headerValue)) { + clientHeader = headerValue.join(", "); + } else { + clientHeader = headerValue; + } + } + } + const additionalValue = additionalHeaders[Headers.ContentType]; + if (additionalValue !== void 0) { + if (typeof additionalValue === "number") { + return String(additionalValue); + } else if (Array.isArray(additionalValue)) { + return additionalValue.join(", "); + } else { + return additionalValue; + } + } + if (clientHeader !== void 0) { + return clientHeader; + } + return _default; } _getAgent(parsedUrl) { let agent; @@ -1332,8 +1417,8 @@ var require_oidc_utils = __commonJS({ return runtimeUrl; } static getCall(id_token_url) { - var _a2; return __awaiter(this, void 0, void 0, function* () { + var _a2; const httpclient = _OidcClient.createHttpClient(); const res = yield httpclient.getJson(id_token_url).catch((error) => { throw new Error(`Failed to get ID Token. @@ -1689,33 +1774,44 @@ var require_path_utils = __commonJS({ } : function(o, v) { o["default"] = v; }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); Object.defineProperty(exports, "__esModule", { value: true }); - exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; + exports.toPosixPath = toPosixPath; + exports.toWin32Path = toWin32Path; + exports.toPlatformPath = toPlatformPath; var path = __importStar(__require("path")); function toPosixPath(pth) { return pth.replace(/[\\]/g, "/"); } - exports.toPosixPath = toPosixPath; function toWin32Path(pth) { return pth.replace(/[/]/g, "\\"); } - exports.toWin32Path = toWin32Path; function toPlatformPath(pth) { return pth.replace(/[/\\]/g, path.sep); } - exports.toPlatformPath = toPlatformPath; } }); @@ -2775,142 +2871,30 @@ var require_platform = __commonJS({ } : function(o, v) { o["default"] = v; }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; - var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P ? value : new P(function(resolve5) { - resolve5(value); - }); - } - return new (P || (P = Promise))(function(resolve5, reject) { - function fulfilled(value) { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - } - function rejected(value) { - try { - step(generator["throw"](value)); - } catch (e) { - reject(e); - } - } - function step(result) { - result.done ? resolve5(result.value) : adopt(result.value).then(fulfilled, rejected); - } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - }; - var __importDefault = exports && exports.__importDefault || function(mod) { - return mod && mod.__esModule ? mod : { "default": mod }; - }; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getDetails = exports.isLinux = exports.isMacOS = exports.isWindows = exports.arch = exports.platform = void 0; - var os_1 = __importDefault(__require("os")); - var exec2 = __importStar(require_exec()); - var getWindowsInfo = () => __awaiter(void 0, void 0, void 0, function* () { - const { stdout: version } = yield exec2.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { - silent: true - }); - const { stdout: name } = yield exec2.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { - silent: true - }); - return { - name: name.trim(), - version: version.trim() - }; - }); - var getMacOsInfo = () => __awaiter(void 0, void 0, void 0, function* () { - var _a2, _b2, _c2, _d; - const { stdout } = yield exec2.getExecOutput("sw_vers", void 0, { - silent: true - }); - const version = (_b2 = (_a2 = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a2 === void 0 ? void 0 : _a2[1]) !== null && _b2 !== void 0 ? _b2 : ""; - const name = (_d = (_c2 = stdout.match(/ProductName:\s*(.+)/)) === null || _c2 === void 0 ? void 0 : _c2[1]) !== null && _d !== void 0 ? _d : ""; - return { - name, - version + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); }; - }); - var getLinuxInfo = () => __awaiter(void 0, void 0, void 0, function* () { - const { stdout } = yield exec2.getExecOutput("lsb_release", ["-i", "-r", "-s"], { - silent: true - }); - const [name, version] = stdout.trim().split("\n"); - return { - name, - version + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; }; - }); - exports.platform = os_1.default.platform(); - exports.arch = os_1.default.arch(); - exports.isWindows = exports.platform === "win32"; - exports.isMacOS = exports.platform === "darwin"; - exports.isLinux = exports.platform === "linux"; - function getDetails() { - return __awaiter(this, void 0, void 0, function* () { - return Object.assign(Object.assign({}, yield exports.isWindows ? getWindowsInfo() : exports.isMacOS ? getMacOsInfo() : getLinuxInfo()), { - platform: exports.platform, - arch: exports.arch, - isWindows: exports.isWindows, - isMacOS: exports.isMacOS, - isLinux: exports.isLinux - }); - }); - } - exports.getDetails = getDetails; - } -}); - -// -var require_core = __commonJS({ - ""(exports) { - "use strict"; - var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) { - if (k2 === void 0) - k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { - return m[k]; - } }; - } - Object.defineProperty(o, k2, desc); - } : function(o, m, k, k2) { - if (k2 === void 0) - k2 = k; - o[k2] = m[k]; - }); - var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); - } : function(o, v) { - o["default"] = v; - }); - var __importStar = exports && exports.__importStar || function(mod) { - if (mod && mod.__esModule) - return mod; - var result = {}; - if (mod != null) { - for (var k in mod) - if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) - __createBinding(result, mod, k); - } - __setModuleDefault(result, mod); - return result; - }; + }(); var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve5) { @@ -2938,8 +2922,165 @@ var require_core = __commonJS({ step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; + var __importDefault = exports && exports.__importDefault || function(mod) { + return mod && mod.__esModule ? mod : { "default": mod }; + }; Object.defineProperty(exports, "__esModule", { value: true }); - exports.platform = exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = exports.markdownSummary = exports.summary = exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; + exports.isLinux = exports.isMacOS = exports.isWindows = exports.arch = exports.platform = void 0; + exports.getDetails = getDetails; + var os_1 = __importDefault(__require("os")); + var exec2 = __importStar(require_exec()); + var getWindowsInfo = () => __awaiter(void 0, void 0, void 0, function* () { + const { stdout: version } = yield exec2.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { + silent: true + }); + const { stdout: name } = yield exec2.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { + silent: true + }); + return { + name: name.trim(), + version: version.trim() + }; + }); + var getMacOsInfo = () => __awaiter(void 0, void 0, void 0, function* () { + var _a2, _b2, _c2, _d; + const { stdout } = yield exec2.getExecOutput("sw_vers", void 0, { + silent: true + }); + const version = (_b2 = (_a2 = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a2 === void 0 ? void 0 : _a2[1]) !== null && _b2 !== void 0 ? _b2 : ""; + const name = (_d = (_c2 = stdout.match(/ProductName:\s*(.+)/)) === null || _c2 === void 0 ? void 0 : _c2[1]) !== null && _d !== void 0 ? _d : ""; + return { + name, + version + }; + }); + var getLinuxInfo = () => __awaiter(void 0, void 0, void 0, function* () { + const { stdout } = yield exec2.getExecOutput("lsb_release", ["-i", "-r", "-s"], { + silent: true + }); + const [name, version] = stdout.trim().split("\n"); + return { + name, + version + }; + }); + exports.platform = os_1.default.platform(); + exports.arch = os_1.default.arch(); + exports.isWindows = exports.platform === "win32"; + exports.isMacOS = exports.platform === "darwin"; + exports.isLinux = exports.platform === "linux"; + function getDetails() { + return __awaiter(this, void 0, void 0, function* () { + return Object.assign(Object.assign({}, yield exports.isWindows ? getWindowsInfo() : exports.isMacOS ? getMacOsInfo() : getLinuxInfo()), { + platform: exports.platform, + arch: exports.arch, + isWindows: exports.isWindows, + isMacOS: exports.isMacOS, + isLinux: exports.isLinux + }); + }); + } + } +}); + +// +var require_core = __commonJS({ + ""(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + o[k2] = m[k]; + }); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) + if (Object.prototype.hasOwnProperty.call(o2, k)) + ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) + if (k[i] !== "default") + __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); + var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function(resolve5) { + resolve5(value); + }); + } + return new (P || (P = Promise))(function(resolve5, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve5(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.platform = exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = exports.markdownSummary = exports.summary = exports.ExitCode = void 0; + exports.exportVariable = exportVariable; + exports.setSecret = setSecret2; + exports.addPath = addPath; + exports.getInput = getInput3; + exports.getMultilineInput = getMultilineInput; + exports.getBooleanInput = getBooleanInput; + exports.setOutput = setOutput; + exports.setCommandEcho = setCommandEcho; + exports.setFailed = setFailed2; + exports.isDebug = isDebug; + exports.debug = debug; + exports.error = error; + exports.warning = warning; + exports.notice = notice; + exports.info = info; + exports.startGroup = startGroup; + exports.endGroup = endGroup; + exports.group = group; + exports.saveState = saveState; + exports.getState = getState; + exports.getIDToken = getIDToken; var command_1 = require_command(); var file_command_1 = require_file_command(); var utils_1 = require_utils(); @@ -2960,11 +3101,9 @@ var require_core = __commonJS({ } (0, command_1.issueCommand)("set-env", { name }, convertedVal); } - exports.exportVariable = exportVariable; function setSecret2(secret) { (0, command_1.issueCommand)("add-mask", {}, secret); } - exports.setSecret = setSecret2; function addPath(inputPath) { const filePath = process.env["GITHUB_PATH"] || ""; if (filePath) { @@ -2974,7 +3113,6 @@ var require_core = __commonJS({ } process.env["PATH"] = `${inputPath}${path.delimiter}${process.env["PATH"]}`; } - exports.addPath = addPath; function getInput3(name, options) { const val = process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || ""; if (options && options.required && !val) { @@ -2985,7 +3123,6 @@ var require_core = __commonJS({ } return val.trim(); } - exports.getInput = getInput3; function getMultilineInput(name, options) { const inputs = getInput3(name, options).split("\n").filter((x) => x !== ""); if (options && options.trimWhitespace === false) { @@ -2993,7 +3130,6 @@ var require_core = __commonJS({ } return inputs.map((input) => input.trim()); } - exports.getMultilineInput = getMultilineInput; function getBooleanInput(name, options) { const trueValue = ["true", "True", "TRUE"]; const falseValue = ["false", "False", "FALSE"]; @@ -3005,7 +3141,6 @@ var require_core = __commonJS({ throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name} Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); } - exports.getBooleanInput = getBooleanInput; function setOutput(name, value) { const filePath = process.env["GITHUB_OUTPUT"] || ""; if (filePath) { @@ -3014,48 +3149,37 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); process.stdout.write(os2.EOL); (0, command_1.issueCommand)("set-output", { name }, (0, utils_1.toCommandValue)(value)); } - exports.setOutput = setOutput; function setCommandEcho(enabled) { (0, command_1.issue)("echo", enabled ? "on" : "off"); } - exports.setCommandEcho = setCommandEcho; function setFailed2(message) { process.exitCode = ExitCode.Failure; error(message); } - exports.setFailed = setFailed2; function isDebug() { return process.env["RUNNER_DEBUG"] === "1"; } - exports.isDebug = isDebug; function debug(message) { (0, command_1.issueCommand)("debug", {}, message); } - exports.debug = debug; function error(message, properties = {}) { (0, command_1.issueCommand)("error", (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); } - exports.error = error; function warning(message, properties = {}) { (0, command_1.issueCommand)("warning", (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); } - exports.warning = warning; function notice(message, properties = {}) { (0, command_1.issueCommand)("notice", (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); } - exports.notice = notice; function info(message) { process.stdout.write(message + os2.EOL); } - exports.info = info; function startGroup(name) { (0, command_1.issue)("group", name); } - exports.startGroup = startGroup; function endGroup() { (0, command_1.issue)("endgroup"); } - exports.endGroup = endGroup; function group(name, fn) { return __awaiter(this, void 0, void 0, function* () { startGroup(name); @@ -3068,7 +3192,6 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); return result; }); } - exports.group = group; function saveState(name, value) { const filePath = process.env["GITHUB_STATE"] || ""; if (filePath) { @@ -3076,17 +3199,14 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); } (0, command_1.issueCommand)("save-state", { name }, (0, utils_1.toCommandValue)(value)); } - exports.saveState = saveState; function getState(name) { return process.env[`STATE_${name}`] || ""; } - exports.getState = getState; function getIDToken(aud) { return __awaiter(this, void 0, void 0, function* () { return yield oidc_utils_1.OidcClient.getIDToken(aud); }); } - exports.getIDToken = getIDToken; var summary_1 = require_summary(); Object.defineProperty(exports, "summary", { enumerable: true, get: function() { return summary_1.summary; @@ -3168,6 +3288,712 @@ var require_context = __commonJS({ } }); +// +var require_proxy2 = __commonJS({ + ""(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.checkBypass = exports.getProxyUrl = void 0; + function getProxyUrl(reqUrl) { + const usingSsl = reqUrl.protocol === "https:"; + if (checkBypass(reqUrl)) { + return void 0; + } + const proxyVar = (() => { + if (usingSsl) { + return process.env["https_proxy"] || process.env["HTTPS_PROXY"]; + } else { + return process.env["http_proxy"] || process.env["HTTP_PROXY"]; + } + })(); + if (proxyVar) { + try { + return new DecodedURL(proxyVar); + } catch (_a2) { + if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://")) + return new DecodedURL(`http://${proxyVar}`); + } + } else { + return void 0; + } + } + exports.getProxyUrl = getProxyUrl; + function checkBypass(reqUrl) { + if (!reqUrl.hostname) { + return false; + } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } + const noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] || ""; + if (!noProxy) { + return false; + } + let reqPort; + if (reqUrl.port) { + reqPort = Number(reqUrl.port); + } else if (reqUrl.protocol === "http:") { + reqPort = 80; + } else if (reqUrl.protocol === "https:") { + reqPort = 443; + } + const upperReqHosts = [reqUrl.hostname.toUpperCase()]; + if (typeof reqPort === "number") { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); + } + for (const upperNoProxyItem of noProxy.split(",").map((x) => x.trim().toUpperCase()).filter((x) => x)) { + if (upperNoProxyItem === "*" || upperReqHosts.some((x) => x === upperNoProxyItem || x.endsWith(`.${upperNoProxyItem}`) || upperNoProxyItem.startsWith(".") && x.endsWith(`${upperNoProxyItem}`))) { + return true; + } + } + return false; + } + exports.checkBypass = checkBypass; + function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return hostLower === "localhost" || hostLower.startsWith("127.") || hostLower.startsWith("[::1]") || hostLower.startsWith("[0:0:0:0:0:0:0:1]"); + } + var DecodedURL = class extends URL { + constructor(url, base) { + super(url, base); + this._decodedUsername = decodeURIComponent(super.username); + this._decodedPassword = decodeURIComponent(super.password); + } + get username() { + return this._decodedUsername; + } + get password() { + return this._decodedPassword; + } + }; + } +}); + +// +var require_lib2 = __commonJS({ + ""(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + o[k2] = m[k]; + }); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || function(mod) { + if (mod && mod.__esModule) + return mod; + var result = {}; + if (mod != null) { + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + } + __setModuleDefault(result, mod); + return result; + }; + var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function(resolve5) { + resolve5(value); + }); + } + return new (P || (P = Promise))(function(resolve5, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve5(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; + var http = __importStar(__require("http")); + var https = __importStar(__require("https")); + var pm = __importStar(require_proxy2()); + var tunnel = __importStar(require_tunnel2()); + var undici_1 = __require("undici"); + var HttpCodes; + (function(HttpCodes2) { + HttpCodes2[HttpCodes2["OK"] = 200] = "OK"; + HttpCodes2[HttpCodes2["MultipleChoices"] = 300] = "MultipleChoices"; + HttpCodes2[HttpCodes2["MovedPermanently"] = 301] = "MovedPermanently"; + HttpCodes2[HttpCodes2["ResourceMoved"] = 302] = "ResourceMoved"; + HttpCodes2[HttpCodes2["SeeOther"] = 303] = "SeeOther"; + HttpCodes2[HttpCodes2["NotModified"] = 304] = "NotModified"; + HttpCodes2[HttpCodes2["UseProxy"] = 305] = "UseProxy"; + HttpCodes2[HttpCodes2["SwitchProxy"] = 306] = "SwitchProxy"; + HttpCodes2[HttpCodes2["TemporaryRedirect"] = 307] = "TemporaryRedirect"; + HttpCodes2[HttpCodes2["PermanentRedirect"] = 308] = "PermanentRedirect"; + HttpCodes2[HttpCodes2["BadRequest"] = 400] = "BadRequest"; + HttpCodes2[HttpCodes2["Unauthorized"] = 401] = "Unauthorized"; + HttpCodes2[HttpCodes2["PaymentRequired"] = 402] = "PaymentRequired"; + HttpCodes2[HttpCodes2["Forbidden"] = 403] = "Forbidden"; + HttpCodes2[HttpCodes2["NotFound"] = 404] = "NotFound"; + HttpCodes2[HttpCodes2["MethodNotAllowed"] = 405] = "MethodNotAllowed"; + HttpCodes2[HttpCodes2["NotAcceptable"] = 406] = "NotAcceptable"; + HttpCodes2[HttpCodes2["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; + HttpCodes2[HttpCodes2["RequestTimeout"] = 408] = "RequestTimeout"; + HttpCodes2[HttpCodes2["Conflict"] = 409] = "Conflict"; + HttpCodes2[HttpCodes2["Gone"] = 410] = "Gone"; + HttpCodes2[HttpCodes2["TooManyRequests"] = 429] = "TooManyRequests"; + HttpCodes2[HttpCodes2["InternalServerError"] = 500] = "InternalServerError"; + HttpCodes2[HttpCodes2["NotImplemented"] = 501] = "NotImplemented"; + HttpCodes2[HttpCodes2["BadGateway"] = 502] = "BadGateway"; + HttpCodes2[HttpCodes2["ServiceUnavailable"] = 503] = "ServiceUnavailable"; + HttpCodes2[HttpCodes2["GatewayTimeout"] = 504] = "GatewayTimeout"; + })(HttpCodes || (exports.HttpCodes = HttpCodes = {})); + var Headers; + (function(Headers2) { + Headers2["Accept"] = "accept"; + Headers2["ContentType"] = "content-type"; + })(Headers || (exports.Headers = Headers = {})); + var MediaTypes; + (function(MediaTypes2) { + MediaTypes2["ApplicationJson"] = "application/json"; + })(MediaTypes || (exports.MediaTypes = MediaTypes = {})); + function getProxyUrl(serverUrl) { + const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); + return proxyUrl ? proxyUrl.href : ""; + } + exports.getProxyUrl = getProxyUrl; + var HttpRedirectCodes = [ + HttpCodes.MovedPermanently, + HttpCodes.ResourceMoved, + HttpCodes.SeeOther, + HttpCodes.TemporaryRedirect, + HttpCodes.PermanentRedirect + ]; + var HttpResponseRetryCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout + ]; + var RetryableHttpVerbs = ["OPTIONS", "GET", "DELETE", "HEAD"]; + var ExponentialBackoffCeiling = 10; + var ExponentialBackoffTimeSlice = 5; + var HttpClientError = class _HttpClientError extends Error { + constructor(message, statusCode) { + super(message); + this.name = "HttpClientError"; + this.statusCode = statusCode; + Object.setPrototypeOf(this, _HttpClientError.prototype); + } + }; + exports.HttpClientError = HttpClientError; + var HttpClientResponse = class { + constructor(message) { + this.message = message; + } + readBody() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve5) => __awaiter(this, void 0, void 0, function* () { + let output = Buffer.alloc(0); + this.message.on("data", (chunk) => { + output = Buffer.concat([output, chunk]); + }); + this.message.on("end", () => { + resolve5(output.toString()); + }); + })); + }); + } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve5) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on("data", (chunk) => { + chunks.push(chunk); + }); + this.message.on("end", () => { + resolve5(Buffer.concat(chunks)); + }); + })); + }); + } + }; + exports.HttpClientResponse = HttpClientResponse; + function isHttps(requestUrl) { + const parsedUrl = new URL(requestUrl); + return parsedUrl.protocol === "https:"; + } + exports.isHttps = isHttps; + var HttpClient = class { + constructor(userAgent2, handlers, requestOptions) { + this._ignoreSslError = false; + this._allowRedirects = true; + this._allowRedirectDowngrade = false; + this._maxRedirects = 50; + this._allowRetries = false; + this._maxRetries = 1; + this._keepAlive = false; + this._disposed = false; + this.userAgent = userAgent2; + this.handlers = handlers || []; + this.requestOptions = requestOptions; + if (requestOptions) { + if (requestOptions.ignoreSslError != null) { + this._ignoreSslError = requestOptions.ignoreSslError; + } + this._socketTimeout = requestOptions.socketTimeout; + if (requestOptions.allowRedirects != null) { + this._allowRedirects = requestOptions.allowRedirects; + } + if (requestOptions.allowRedirectDowngrade != null) { + this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; + } + if (requestOptions.maxRedirects != null) { + this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); + } + if (requestOptions.keepAlive != null) { + this._keepAlive = requestOptions.keepAlive; + } + if (requestOptions.allowRetries != null) { + this._allowRetries = requestOptions.allowRetries; + } + if (requestOptions.maxRetries != null) { + this._maxRetries = requestOptions.maxRetries; + } + } + } + options(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("OPTIONS", requestUrl, null, additionalHeaders || {}); + }); + } + get(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("GET", requestUrl, null, additionalHeaders || {}); + }); + } + del(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("DELETE", requestUrl, null, additionalHeaders || {}); + }); + } + post(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("POST", requestUrl, data, additionalHeaders || {}); + }); + } + patch(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("PATCH", requestUrl, data, additionalHeaders || {}); + }); + } + put(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("PUT", requestUrl, data, additionalHeaders || {}); + }); + } + head(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request("HEAD", requestUrl, null, additionalHeaders || {}); + }); + } + sendStream(verb, requestUrl, stream, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request(verb, requestUrl, stream, additionalHeaders); + }); + } + /** + * Gets a typed object from an endpoint + * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise + */ + getJson(requestUrl, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + const res = yield this.get(requestUrl, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + postJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.post(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + putJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.put(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + patchJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.patch(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + /** + * Makes a raw http request. + * All other methods such as get, post, patch, and request ultimately call this. + * Prefer get, del, post and patch + */ + request(verb, requestUrl, data, headers) { + return __awaiter(this, void 0, void 0, function* () { + if (this._disposed) { + throw new Error("Client has already been disposed."); + } + const parsedUrl = new URL(requestUrl); + let info = this._prepareRequest(verb, parsedUrl, headers); + const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) ? this._maxRetries + 1 : 1; + let numTries = 0; + let response; + do { + response = yield this.requestRaw(info, data); + if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { + let authenticationHandler; + for (const handler2 of this.handlers) { + if (handler2.canHandleAuthentication(response)) { + authenticationHandler = handler2; + break; + } + } + if (authenticationHandler) { + return authenticationHandler.handleAuthentication(this, info, data); + } else { + return response; + } + } + let redirectsRemaining = this._maxRedirects; + while (response.message.statusCode && HttpRedirectCodes.includes(response.message.statusCode) && this._allowRedirects && redirectsRemaining > 0) { + const redirectUrl = response.message.headers["location"]; + if (!redirectUrl) { + break; + } + const parsedRedirectUrl = new URL(redirectUrl); + if (parsedUrl.protocol === "https:" && parsedUrl.protocol !== parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) { + throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true."); + } + yield response.readBody(); + if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { + for (const header in headers) { + if (header.toLowerCase() === "authorization") { + delete headers[header]; + } + } + } + info = this._prepareRequest(verb, parsedRedirectUrl, headers); + response = yield this.requestRaw(info, data); + redirectsRemaining--; + } + if (!response.message.statusCode || !HttpResponseRetryCodes.includes(response.message.statusCode)) { + return response; + } + numTries += 1; + if (numTries < maxTries) { + yield response.readBody(); + yield this._performExponentialBackoff(numTries); + } + } while (numTries < maxTries); + return response; + }); + } + /** + * Needs to be called if keepAlive is set to true in request options. + */ + dispose() { + if (this._agent) { + this._agent.destroy(); + } + this._disposed = true; + } + /** + * Raw request. + * @param info + * @param data + */ + requestRaw(info, data) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve5, reject) => { + function callbackForResult(err, res) { + if (err) { + reject(err); + } else if (!res) { + reject(new Error("Unknown error")); + } else { + resolve5(res); + } + } + this.requestRawWithCallback(info, data, callbackForResult); + }); + }); + } + /** + * Raw request with callback. + * @param info + * @param data + * @param onResult + */ + requestRawWithCallback(info, data, onResult) { + if (typeof data === "string") { + if (!info.options.headers) { + info.options.headers = {}; + } + info.options.headers["Content-Length"] = Buffer.byteLength(data, "utf8"); + } + let callbackCalled = false; + function handleResult(err, res) { + if (!callbackCalled) { + callbackCalled = true; + onResult(err, res); + } + } + const req = info.httpModule.request(info.options, (msg) => { + const res = new HttpClientResponse(msg); + handleResult(void 0, res); + }); + let socket; + req.on("socket", (sock) => { + socket = sock; + }); + req.setTimeout(this._socketTimeout || 3 * 6e4, () => { + if (socket) { + socket.end(); + } + handleResult(new Error(`Request timeout: ${info.options.path}`)); + }); + req.on("error", function(err) { + handleResult(err); + }); + if (data && typeof data === "string") { + req.write(data, "utf8"); + } + if (data && typeof data !== "string") { + data.on("close", function() { + req.end(); + }); + data.pipe(req); + } else { + req.end(); + } + } + /** + * Gets an http agent. This function is useful when you need an http agent that handles + * routing through a proxy server - depending upon the url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ + getAgent(serverUrl) { + const parsedUrl = new URL(serverUrl); + return this._getAgent(parsedUrl); + } + getAgentDispatcher(serverUrl) { + const parsedUrl = new URL(serverUrl); + const proxyUrl = pm.getProxyUrl(parsedUrl); + const useProxy = proxyUrl && proxyUrl.hostname; + if (!useProxy) { + return; + } + return this._getProxyAgentDispatcher(parsedUrl, proxyUrl); + } + _prepareRequest(method, requestUrl, headers) { + const info = {}; + info.parsedUrl = requestUrl; + const usingSsl = info.parsedUrl.protocol === "https:"; + info.httpModule = usingSsl ? https : http; + const defaultPort = usingSsl ? 443 : 80; + info.options = {}; + info.options.host = info.parsedUrl.hostname; + info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort; + info.options.path = (info.parsedUrl.pathname || "") + (info.parsedUrl.search || ""); + info.options.method = method; + info.options.headers = this._mergeHeaders(headers); + if (this.userAgent != null) { + info.options.headers["user-agent"] = this.userAgent; + } + info.options.agent = this._getAgent(info.parsedUrl); + if (this.handlers) { + for (const handler2 of this.handlers) { + handler2.prepareRequest(info.options); + } + } + return info; + } + _mergeHeaders(headers) { + if (this.requestOptions && this.requestOptions.headers) { + return Object.assign({}, lowercaseKeys2(this.requestOptions.headers), lowercaseKeys2(headers || {})); + } + return lowercaseKeys2(headers || {}); + } + _getExistingOrDefaultHeader(additionalHeaders, header, _default) { + let clientHeader; + if (this.requestOptions && this.requestOptions.headers) { + clientHeader = lowercaseKeys2(this.requestOptions.headers)[header]; + } + return additionalHeaders[header] || clientHeader || _default; + } + _getAgent(parsedUrl) { + let agent; + const proxyUrl = pm.getProxyUrl(parsedUrl); + const useProxy = proxyUrl && proxyUrl.hostname; + if (this._keepAlive && useProxy) { + agent = this._proxyAgent; + } + if (!useProxy) { + agent = this._agent; + } + if (agent) { + return agent; + } + const usingSsl = parsedUrl.protocol === "https:"; + let maxSockets = 100; + if (this.requestOptions) { + maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; + } + if (proxyUrl && proxyUrl.hostname) { + const agentOptions = { + maxSockets, + keepAlive: this._keepAlive, + proxy: Object.assign(Object.assign({}, (proxyUrl.username || proxyUrl.password) && { + proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` + }), { host: proxyUrl.hostname, port: proxyUrl.port }) + }; + let tunnelAgent; + const overHttps = proxyUrl.protocol === "https:"; + if (usingSsl) { + tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; + } else { + tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; + } + agent = tunnelAgent(agentOptions); + this._proxyAgent = agent; + } + if (!agent) { + const options = { keepAlive: this._keepAlive, maxSockets }; + agent = usingSsl ? new https.Agent(options) : new http.Agent(options); + this._agent = agent; + } + if (usingSsl && this._ignoreSslError) { + agent.options = Object.assign(agent.options || {}, { + rejectUnauthorized: false + }); + } + return agent; + } + _getProxyAgentDispatcher(parsedUrl, proxyUrl) { + let proxyAgent; + if (this._keepAlive) { + proxyAgent = this._proxyAgentDispatcher; + } + if (proxyAgent) { + return proxyAgent; + } + const usingSsl = parsedUrl.protocol === "https:"; + proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl.href, pipelining: !this._keepAlive ? 0 : 1 }, (proxyUrl.username || proxyUrl.password) && { + token: `Basic ${Buffer.from(`${proxyUrl.username}:${proxyUrl.password}`).toString("base64")}` + })); + this._proxyAgentDispatcher = proxyAgent; + if (usingSsl && this._ignoreSslError) { + proxyAgent.options = Object.assign(proxyAgent.options.requestTls || {}, { + rejectUnauthorized: false + }); + } + return proxyAgent; + } + _performExponentialBackoff(retryNumber) { + return __awaiter(this, void 0, void 0, function* () { + retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); + const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); + return new Promise((resolve5) => setTimeout(() => resolve5(), ms)); + }); + } + _processResponse(res, options) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve5, reject) => __awaiter(this, void 0, void 0, function* () { + const statusCode = res.message.statusCode || 0; + const response = { + statusCode, + result: null, + headers: {} + }; + if (statusCode === HttpCodes.NotFound) { + resolve5(response); + } + function dateTimeDeserializer(key, value) { + if (typeof value === "string") { + const a = new Date(value); + if (!isNaN(a.valueOf())) { + return a; + } + } + return value; + } + let obj; + let contents; + try { + contents = yield res.readBody(); + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, dateTimeDeserializer); + } else { + obj = JSON.parse(contents); + } + response.result = obj; + } + response.headers = res.message.headers; + } catch (err) { + } + if (statusCode > 299) { + let msg; + if (obj && obj.message) { + msg = obj.message; + } else if (contents && contents.length > 0) { + msg = contents; + } else { + msg = `Failed request: (${statusCode})`; + } + const err = new HttpClientError(msg, statusCode); + err.result = response.result; + reject(err); + } else { + resolve5(response); + } + })); + }); + } + }; + exports.HttpClient = HttpClient; + var lowercaseKeys2 = (obj) => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {}); + } +}); + // var require_utils2 = __commonJS({ ""(exports) { @@ -3233,7 +4059,7 @@ var require_utils2 = __commonJS({ }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getApiBaseUrl = exports.getProxyFetch = exports.getProxyAgentDispatcher = exports.getProxyAgent = exports.getAuthString = void 0; - var httpClient = __importStar(require_lib()); + var httpClient = __importStar(require_lib2()); var undici_1 = __require("undici"); function getAuthString(token, options) { if (!token && !options.auth) { @@ -10092,7 +10918,7 @@ var require_cjs = __commonJS({ }); // -var require_lib2 = __commonJS({ +var require_lib3 = __commonJS({ ""(exports, module) { var { isexe, sync: isexeSync } = require_cjs(); var { join: join6, delimiter, sep: sep2, posix } = __require("path"); @@ -18753,7 +19579,7 @@ var require_Alias = __commonJS({ toJS.toJS(source, null, ctx); data = anchors2.get(source); } - if (!data || data.res === void 0) { + if (data?.res === void 0) { const msg = "This should not happen: Alias anchor was not resolved?"; throw new ReferenceError(msg); } @@ -19703,7 +20529,7 @@ ${indent}:`; ${stringifyComment.indentComment(cs, ctx.indent)}`; } if (valueStr === "" && !ctx.inFlow) { - if (ws === "\n") + if (ws === "\n" && valueComment) ws = "\n\n"; } else { ws += ` @@ -20448,7 +21274,7 @@ var require_stringifyNumber = __commonJS({ const num = typeof value === "number" ? value : Number(value); if (!isFinite(num)) return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; - let n = JSON.stringify(value); + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); if (!format3 && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) { let i = n.indexOf("."); if (i < 0) { @@ -21816,7 +22642,7 @@ var require_errors = __commonJS({ if (/[^ ]/.test(lineStr)) { let count = 1; const end = error.linePos[1]; - if (end && end.line === line && end.col > col) { + if (end?.line === line && end.col > col) { count = Math.max(1, Math.min(end.col - col, 80 - ci)); } const pointer = " ".repeat(ci) + "^".repeat(count); @@ -22178,7 +23004,7 @@ var require_resolve_block_seq = __commonJS({ }); if (!props.found) { if (props.anchor || props.tag || value) { - if (value && value.type === "block-seq") + if (value?.type === "block-seq") onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); else onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); @@ -22376,7 +23202,7 @@ var require_resolve_flow_collection = __commonJS({ onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); } } else if (value) { - if ("source" in value && value.source && value.source[0] === ":") + if ("source" in value && value.source?.[0] === ":") onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); else onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); @@ -22413,7 +23239,7 @@ var require_resolve_flow_collection = __commonJS({ const expectedEnd = isMap ? "}" : "]"; const [ce, ...ee] = fc.end; let cePos = offset; - if (ce && ce.source === expectedEnd) + if (ce?.source === expectedEnd) cePos = ce.offset + ce.source.length; else { const name = fcName[0].toUpperCase() + fcName.substring(1); @@ -22480,7 +23306,7 @@ var require_compose_collection = __commonJS({ let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); if (!tag) { const kt = ctx.schema.knownTags[tagName]; - if (kt && kt.collection === expType) { + if (kt?.collection === expType) { ctx.schema.tags.push(Object.assign({}, kt, { default: false })); tag = kt; } else { @@ -24570,7 +25396,7 @@ var require_parser = __commonJS({ } *step() { const top2 = this.peek(1); - if (this.type === "doc-end" && (!top2 || top2.type !== "doc-end")) { + if (this.type === "doc-end" && top2?.type !== "doc-end") { while (this.stack.length > 0) yield* this.pop(); this.stack.push({ @@ -25046,7 +25872,7 @@ var require_parser = __commonJS({ do { yield* this.pop(); top2 = this.peek(1); - } while (top2 && top2.type === "flow-collection"); + } while (top2?.type === "flow-collection"); } else if (fc.end.length === 0) { switch (this.type) { case "comma": @@ -25696,8 +26522,8 @@ function processAsyncCmd(command2, options, childProcess) { }); childProcess.on("close", (exitCode, signal) => { const exitDescription = exitCode !== null ? `exit code "${exitCode}"` : `signal "${signal}"`; - const printFn = options.mode === "on-error" ? Log.error : Log.debug; const status = statusFromExitCodeAndSignal(exitCode, signal); + const printFn = status !== 0 && options.mode === "on-error" ? Log.error : Log.debug; printFn(`Command "${command2}" completed with ${exitDescription}.`); printFn(`Process output: ${logOutput}`); @@ -26834,7 +27660,7 @@ var YargsParser = class { } applyEnvVars(argv, true); applyEnvVars(argv, false); - setConfig2(argv); + setConfig(argv); setConfigObjects(); applyDefaultsAndAliases(argv, flags.aliases, defaults, true); applyCoercions(argv); @@ -27024,7 +27850,7 @@ var YargsParser = class { } return value; } - function setConfig2(argv2) { + function setConfig(argv2) { const configLookup = /* @__PURE__ */ Object.create(null); applyDefaultsAndAliases(configLookup, flags.aliases, defaults); Object.keys(flags.configs).forEach(function(configKey) { @@ -31081,7 +31907,6 @@ function getCachedConfig() { // import { pathToFileURL } from "url"; var CONFIG_FILE_PATH_MATCHER = ".ng-dev/config.mjs"; -var setConfig = setCachedConfig; async function getConfig(baseDirOrAssertions) { let cachedConfig2 = getCachedConfig(); if (cachedConfig2 === null) { @@ -31119,6 +31944,9 @@ function assertValidGithubConfig(config) { if (config.github.owner === void 0) { errors.push(`"github.owner" is not defined`); } + if (config.github.mergeMode === void 0) { + errors.push(`"github.mergeMode" is not defined`); + } } if (errors.length) { throw new ConfigValidationError("Invalid `github` configuration", errors); @@ -35270,6 +36098,7 @@ var GithubClient = class { this.rest = this._octokit.rest; this.paginate = this._octokit.paginate; this.checks = this._octokit.checks; + this.users = this._octokit.users; } }; var AuthenticatedGithubClient = class extends GithubClient { @@ -35502,7 +36331,7 @@ AuthenticatedGitClient._token = null; AuthenticatedGitClient._authenticatedInstance = null; // -var import_which = __toESM(require_lib2()); +var import_which = __toESM(require_lib3()); var import_lockfile = __toESM(require_lockfile()); var import_yaml = __toESM(require_dist2()); @@ -35591,13 +36420,7 @@ import { tmpdir as tmpdir2 } from "os"; import { join as join5 } from "path"; var refMatcher = /refs\/heads\/(.*)/; async function deployDocs() { - setConfig({ - github: { - mainBranchName: "main", - name: "angular", - owner: "angular" - } - }); + getConfig([assertValidGithubConfig]); AuthenticatedGitClient.configure(githubReleaseTrainReadToken); if (import_github3.context.eventName !== "push") { throw Error(); diff --git a/.github/actions/saucelabs-legacy/action.yml b/.github/actions/saucelabs-legacy/action.yml index e592789951d6..3113150fe186 100644 --- a/.github/actions/saucelabs-legacy/action.yml +++ b/.github/actions/saucelabs-legacy/action.yml @@ -5,9 +5,9 @@ runs: using: 'composite' steps: - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Saucelabs Variables - uses: angular/dev-infra/github-actions/saucelabs@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/saucelabs@dd5658bd720370542913e23b21d80450bac94b60 - name: Starting Saucelabs tunnel service shell: bash run: ./tools/saucelabs/sauce-service.sh run & diff --git a/.github/workflows/adev-preview-build.yml b/.github/workflows/adev-preview-build.yml index 388618bd04f7..d4b96435e3f9 100644 --- a/.github/workflows/adev-preview-build.yml +++ b/.github/workflows/adev-preview-build.yml @@ -21,17 +21,17 @@ jobs: (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview')) steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Build adev # `snapshot-build` config is used to stamp the exact version with sha in the footer. run: pnpm bazel build //adev:build.production --config=snapshot-build - - uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@dd5658bd720370542913e23b21d80450bac94b60 with: workflow-artifact-name: 'adev-preview' pull-number: '${{github.event.pull_request.number}}' diff --git a/.github/workflows/adev-preview-deploy.yml b/.github/workflows/adev-preview-deploy.yml index d705103088d5..1a316667fdbb 100644 --- a/.github/workflows/adev-preview-deploy.yml +++ b/.github/workflows/adev-preview-deploy.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 with: token: '${{secrets.GITHUB_TOKEN}}' @@ -40,7 +40,7 @@ jobs: npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}} - - uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@dd5658bd720370542913e23b21d80450bac94b60 with: github-token: '${{secrets.GITHUB_TOKEN}}' workflow-artifact-name: 'adev-preview' diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml index 43f71577f113..30ef371eab5c 100644 --- a/.github/workflows/assistant-to-the-branch-manager.yml +++ b/.github/workflows/assistant-to-the-branch-manager.yml @@ -12,10 +12,11 @@ permissions: jobs: assistant_to_the_branch_manager: runs-on: ubuntu-latest + if: github.event.repository.fork == false steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - - uses: angular/dev-infra/github-actions/branch-manager@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: angular/dev-infra/github-actions/branch-manager@dd5658bd720370542913e23b21d80450bac94b60 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/benchmark-compare.yml b/.github/workflows/benchmark-compare.yml index 3ad7cd61a755..78a70f0a8de3 100644 --- a/.github/workflows/benchmark-compare.yml +++ b/.github/workflows/benchmark-compare.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/benchmark-compare ')}} steps: - - uses: TheModdingInquisition/actions-team-membership@a69636a92bc927f32c3910baac06bacc949c984c # v1.0 + - uses: TheModdingInquisition/actions-team-membership@057d91bb80f2976a1bc6dfab5b4ae1da9aebbd89 # v1.0 with: team: 'team' organization: angular @@ -28,7 +28,7 @@ jobs: - uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1 id: comment-branch - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: # Specify repository as the PR branch might be from a fork. repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}} @@ -38,7 +38,7 @@ jobs: - run: pnpm install --frozen-lockfile - - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: bazelrc: ./.bazelrc.user diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55dcb0bb5078..7b1425f57816 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Check code lint @@ -39,13 +39,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 with: disable-package-manager-cache: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -55,7 +55,7 @@ jobs: - name: Test build run: pnpm devtools:build:chrome - name: Cypress run - uses: cypress-io/github-action@7ef72e250a9e564efb4ed4c2433971ada4cc38b4 # v6.10.4 + uses: cypress-io/github-action@2ad32e649e4db26c07674ebae31a297601dbcbaf # v6.10.8 with: command: pnpm devtools:test:e2e start: pnpm bazel run //devtools/src:devserver @@ -67,11 +67,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -83,11 +83,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -100,11 +100,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -119,11 +119,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -136,11 +136,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials @@ -152,11 +152,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -206,11 +206,11 @@ jobs: runs-on: ubuntu-latest-8core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Build adev diff --git a/.github/workflows/cross-repo-adev-docs.yml b/.github/workflows/cross-repo-adev-docs.yml index 67f96d9a3c82..896dc3e52a16 100644 --- a/.github/workflows/cross-repo-adev-docs.yml +++ b/.github/workflows/cross-repo-adev-docs.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout the repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: # Setting `persist-credentials: false` prevents the github-action account from being the # account that is attempted to be used for authentication, instead the remote is set to @@ -37,7 +37,7 @@ jobs: ANGULAR_READONLY_GITHUB_TOKEN: ${{ secrets.READONLY_GITHUB_TOKEN }} - name: Create a PR (if necessary) - uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9 + uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0 with: token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }} push-to-fork: 'angular-robot/angular' diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index 2fa680b35016..fa6a8bc2dd67 100644 --- a/.github/workflows/dev-infra.yml +++ b/.github/workflows/dev-infra.yml @@ -12,14 +12,14 @@ jobs: labels: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - - uses: angular/dev-infra/github-actions/pull-request-labeling@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: angular/dev-infra/github-actions/pull-request-labeling@dd5658bd720370542913e23b21d80450bac94b60 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} post_approval_changes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - - uses: angular/dev-infra/github-actions/post-approval-changes@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: angular/dev-infra/github-actions/post-approval-changes@dd5658bd720370542913e23b21d80450bac94b60 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/google-internal-tests.yml b/.github/workflows/google-internal-tests.yml index 32990de791ea..d497d99d03a2 100644 --- a/.github/workflows/google-internal-tests.yml +++ b/.github/workflows/google-internal-tests.yml @@ -13,8 +13,8 @@ jobs: statuses: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - - uses: angular/dev-infra/github-actions/google-internal-tests@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: angular/dev-infra/github-actions/google-internal-tests@dd5658bd720370542913e23b21d80450bac94b60 with: run-tests-guide-url: http://go/angular-g3sync-start github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index 879ce602a691..8b488848828a 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -13,15 +13,15 @@ jobs: JOBS: 2 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Saucelabs Variables - uses: angular/dev-infra/github-actions/saucelabs@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/saucelabs@dd5658bd720370542913e23b21d80450bac94b60 - name: Set up Sauce Tunnel Daemon run: pnpm bazel run //tools/saucelabs-daemon/background-service -- $JOBS & env: diff --git a/.github/workflows/merge-ready-status.yml b/.github/workflows/merge-ready-status.yml index c1c743178ef0..89f967723dc5 100644 --- a/.github/workflows/merge-ready-status.yml +++ b/.github/workflows/merge-ready-status.yml @@ -9,6 +9,6 @@ jobs: status: runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/unified-status-check@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + - uses: angular/dev-infra/github-actions/unified-status-check@dd5658bd720370542913e23b21d80450bac94b60 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index cb20b81b1dd7..11c031ef1fc9 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -21,7 +21,7 @@ jobs: workflows: ${{ steps.workflows.outputs.workflows }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - id: workflows @@ -36,9 +36,9 @@ jobs: workflow: ${{ fromJSON(needs.list.outputs.workflows) }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile # We utilize the google-github-actions/auth action to allow us to get an active credential using workflow diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 552aec3e3175..5ce846433c10 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Check code lint @@ -37,7 +37,7 @@ jobs: - name: Check code format run: pnpm ng-dev format changed --check ${{ github.event.pull_request.base.sha }} - name: Check Package Licenses - uses: angular/dev-infra/github-actions/linting/licenses@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/linting/licenses@dd5658bd720370542913e23b21d80450bac94b60 with: allow-dependencies-licenses: 'pkg:npm/google-protobuf@' @@ -45,13 +45,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 with: disable-package-manager-cache: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Run unit tests @@ -59,7 +59,7 @@ jobs: - name: Test build run: pnpm devtools:build:chrome - name: Cypress run - uses: cypress-io/github-action@7ef72e250a9e564efb4ed4c2433971ada4cc38b4 # v6.10.4 + uses: cypress-io/github-action@2ad32e649e4db26c07674ebae31a297601dbcbaf # v6.10.8 with: command: pnpm devtools:test:e2e start: pnpm bazel run //devtools/src:devserver @@ -71,11 +71,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Run CI tests for framework @@ -86,7 +86,7 @@ jobs: ASPECT_RULES_JS_FROZEN_PNPM_LOCK: '1' - name: Upload GRPC logs (for debugging of RBE issues) if: always() - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: path: /tmp/rbe-grpc.log retention-days: 1 @@ -95,11 +95,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Run integration CI tests for framework @@ -110,11 +110,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Run tests @@ -127,11 +127,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - name: Run tests @@ -142,11 +142,11 @@ jobs: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/setup@dd5658bd720370542913e23b21d80450bac94b60 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + uses: angular/dev-infra/github-actions/bazel/configure-remote@dd5658bd720370542913e23b21d80450bac94b60 - name: Install node modules run: pnpm install --frozen-lockfile - run: | diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 9de992de9d8f..94274a52d0d5 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -25,7 +25,7 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -39,7 +39,7 @@ jobs: # Upload the results as artifacts. - name: 'Upload artifact' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: sarif_file: results.sarif diff --git a/.ng-dev/format.mjs b/.ng-dev/format.mjs index 53074fadc2ac..c750eeea395c 100644 --- a/.ng-dev/format.mjs +++ b/.ng-dev/format.mjs @@ -4,35 +4,6 @@ * @type { import("@angular/ng-dev").FormatConfig } */ export const format = { - 'prettier': { - 'matchers': [ - '**/*.md', - '**/*.{yaml,yml}', - '**/*.{js,ts,mjs,mts,cjs,cts,tsx}', - 'devtools/**/*.{js,ts,mjs,mts,cjs,cts,html,scss}', - 'integration/**/size.json', - // Do not format d.ts files as they are generated - '!**/*.d.ts', - // Both third_party and .yarn are directories containing copied code which should - // not be modified. - '!third_party/**', - // Do not format the locale files which are checked-in for Google3, but generated using - // the `generate-locales-tool` from `packages/common/locales`. - '!packages/core/src/i18n/locale_en.ts', - '!packages/common/locales/closure-locale.ts', - '!packages/common/src/i18n/currencies.ts', - // Test cases contain non valid code. - '!packages/compiler-cli/test/compliance/test_cases/**/*.{js,ts,mjs,mts,cjs,cts}', - - // Ignore generated javascript file(s) - '!.github/actions/deploy-docs-site/main.js', - - // Ignore testing data files for language service - '!vscode-ng-language-service/syntaxes/test/data/*.ts', - - // Ignore goldens MD files - '!/goldens/**/*.api.md', - ], - }, + 'prettier': true, 'buildifier': true, }; diff --git a/.ng-dev/github.mjs b/.ng-dev/github.mjs index c1403ae90a33..57b585723a7b 100644 --- a/.ng-dev/github.mjs +++ b/.ng-dev/github.mjs @@ -8,5 +8,5 @@ export const github = { owner: 'angular', name: 'angular', mainBranchName: 'main', - useNgDevAuthService: true, + mergeMode: 'caretaker-only', }; diff --git a/.ng-dev/google-sync-config.json b/.ng-dev/google-sync-config.json index 89d77442a048..a3e5f12193db 100644 --- a/.ng-dev/google-sync-config.json +++ b/.ng-dev/google-sync-config.json @@ -32,6 +32,7 @@ "**/rollup.config.js", "**/BUILD.bazel", "**/*.md", + "**/package.json", "packages/**/integrationtest/**", "packages/**/test/**", "packages/zone.js/*", diff --git a/.prettierignore b/.prettierignore index 4f17af43e3a6..cbb23cdee155 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,38 @@ -.prettierrc +pnpm-lock.yaml -pnpm-lock.yaml \ No newline at end of file +# Do not format d.ts files as they are generated +**/*.d.ts + +# Both third_party and .yarn are directories containing copied code which should +# not be modified. +third_party/**/*.js +third_party/**/*.css +third_party/**/*.md +third_party/**/*.json + +# Do not format the locale files which are checked-in for Google3, but generated using +# the `generate-locales-tool` from `packages/common/locales`. +packages/core/src/i18n/locale_en.ts +packages/common/locales/closure-locale.ts +packages/common/src/i18n/currencies.ts + +# Test cases contain non valid code. +packages/compiler-cli/test/compliance/test_cases/**/*.js +packages/compiler-cli/test/compliance/test_cases/**/*.ts + +# Ignore generated javascript file(s) +.github/actions/deploy-docs-site/main.js + +# Ignore testing data files for language service +vscode-ng-language-service/syntaxes/test/data/*.ts + +# Ignore goldens MD files +goldens/**/*.api.md + +# adev generated files +adev/src/content/aria/**/*.json +adev/src/content/cli/**/*.json +adev/src/content/cdk/**/*.json + +CHANGELOG.md +CHANGELOG_ARCHIVE.md \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index ed2ad7dc637d..7f029d260104 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,10 +2,24 @@ "printWidth": 100, "tabWidth": 2, "useTabs": false, - "embeddedLanguageFormatting": "off", "singleQuote": true, "semi": true, "quoteProps": "preserve", "bracketSpacing": false, - "trailingComma": "all" + "trailingComma": "all", + "overrides": [ + { + "files": ["./.prettierrc"], + "options": { + "parser": "json" + } + }, + { + "files": ["*.html"], + "excludeFiles": ["**/test/**"], + "options": { + "parser": "angular" + } + } + ] } diff --git a/.pullapprove.yml b/.pullapprove.yml index 9ef09733b88f..767101fc2ad5 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -121,6 +121,7 @@ groups: - > contains_any_globs(files.exclude('packages/core/primitives/*'), [ 'contributing-docs/public-api-surface.md', + 'dev-app/**/{*,.*}', 'integration/**/{*,.*}', 'modules/**/{*,.*}', 'packages/animations/**/{*,.*}', diff --git a/CHANGELOG.md b/CHANGELOG.md index 253c9db9df7b..bebc50694d7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,325 @@ + +# 21.1.0-next.4 (2025-12-17) +## Breaking Changes +### forms +- The shape of `SignalFormsConfig.classes` has changed + + Previously each function in the `classes` map took a `FieldState`. Now + it takes a `Field` directive. + + For example if you previously had: + ``` + provideSignalFormsConfig({ + classes: { + 'my-valid': (state) => state.valid() + } + }) + ``` + + You would need to update to: + ``` + provideSignalFormsConfig({ + classes: { + 'my-valid': ({state}) => state().valid() + } + }) + ``` +- +### core +| Commit | Type | Description | +| -- | -- | -- | +| [06be8034bb](https://github.com/angular/angular/commit/06be8034bb9b9adfc07ab0d40cd87c6ae5de02de) | fix | Microtask scheduling should be used after any application synchronization | +| [b4f584cf42](https://github.com/angular/angular/commit/b4f584cf42235c94bb8389fa55bc634e23d7b010) | fix | return `StaticProvider` for `providePlatformInitializer` | +| [7be4ddef1c](https://github.com/angular/angular/commit/7be4ddef1ccb9a6b330b52b9bbdd174089755503) | fix | throw better errors for potential circular references | +| [f516370c8e](https://github.com/angular/angular/commit/f516370c8e8e96806bad757d43c857ab5f2051ed) | fix | use mutable ResponseInit type for RESPONSE_INIT token | +### forms +| Commit | Type | Description | +| -- | -- | -- | +| [348f149e8b](https://github.com/angular/angular/commit/348f149e8b06d6885f54bac4cf03a9481a8b19b7) | feat | pass field directive to class config | +| [ae0c59028a](https://github.com/angular/angular/commit/ae0c59028a2f393ea5716bf222db2c38e7a3989f) | refactor | rename field to fieldTree in FieldContext and ValidationError | +### language-service +| Commit | Type | Description | +| -- | -- | -- | +| [9f5744a92d](https://github.com/angular/angular/commit/9f5744a92dda4219e93bf7e6236d32a93bc167e6) | fix | avoid interpolation highlighting inside @let | +| [ce1a4769f9](https://github.com/angular/angular/commit/ce1a4769f987e96e2a1b1cbc7654ed5bac9ce91d) | fix | Prevent language service from crashing on suggestion diagnostic errors | + + + + +# 21.0.6 (2025-12-17) +## Breaking Changes (affecting only experimental features) +### forms +- The shape of `SignalFormsConfig.classes` has changed + + Previously each function in the `classes` map took a `FieldState`. Now + it takes a `Field` directive. + + For example if you previously had: + ``` + provideSignalFormsConfig({ + classes: { + 'my-valid': (state) => state.valid() + } + }) + ``` + + You would need to update to: + ``` + provideSignalFormsConfig({ + classes: { + 'my-valid': ({state}) => state().valid() + } + }) + ``` + + (cherry picked from commit 348f149e8b06d6885f54bac4cf03a9481a8b19b7) +- (cherry picked from commit ae0c59028a2f393ea5716bf222db2c38e7a3989f) +### core +| Commit | Type | Description | +| -- | -- | -- | +| [4c8fb3631d](https://github.com/angular/angular/commit/4c8fb3631d58e22d693aba0b89243f2e9ecb0807) | fix | throw better errors for potential circular references | +| [48492524ea](https://github.com/angular/angular/commit/48492524ea4adfa232b0daee0d955924be31ebea) | fix | use mutable ResponseInit type for RESPONSE_INIT token | +### forms +| Commit | Type | Description | +| -- | -- | -- | +| [81772b420d](https://github.com/angular/angular/commit/81772b420dcda2cbe2a8cb75e50c6da2e1ecdc68) | feat | pass field directive to class config | +| [729b96476b](https://github.com/angular/angular/commit/729b96476b73f1670a0f7c6ab3f36be9d38ebcac) | refactor | rename field to fieldTree in FieldContext and ValidationError | +### language-service +| Commit | Type | Description | +| -- | -- | -- | +| [e0694df3ec](https://github.com/angular/angular/commit/e0694df3eccae3d31a4ea537dffe1db1368ef34a) | fix | avoid interpolation highlighting inside @let | +| [5047be4bc1](https://github.com/angular/angular/commit/5047be4bc1c6f6016263703c743f8033f669f0ee) | fix | Prevent language service from crashing on suggestion diagnostic errors | + + + + + +# 21.1.0-next.3 (2025-12-11) + +### core + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------ | +| [4f6014a756a](https://github.com/angular/angular/commit/4f6014a756a95ff9cb55e91deb27039b7eb079ee) | fix | avoid false-positive deprecation when using `InjectionToken` with factory only | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------- | +| [d0097f7d0c2](https://github.com/angular/angular/commit/d0097f7d0c214c5c3d715e7f1dfa3723b31f4065) | fix | fix signal forms type error | + + + + + +# 21.0.5 (2025-12-11) + +### core + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------ | +| [69d243abb74](https://github.com/angular/angular/commit/69d243abb7438c37b9ef763755f8fb7fdee165be) | fix | avoid false-positive deprecation when using `InjectionToken` with factory only | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------- | +| [4fd2b722b40](https://github.com/angular/angular/commit/4fd2b722b4054181a6e5f09a3cc657ae05541782) | fix | fix signal forms type error | + + + + + +# 21.1.0-next.2 (2025-12-10) + +## Deprecations + +### upgrade + +- `VERSION` from `@angular/upgrade` is deprecated. Please use the entry from `@angular/upgrade/static` instead. + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------------- | +| [ae1c0dc4900](https://github.com/angular/angular/commit/ae1c0dc49002665c10d4e44f530f4cb8d1e35b8d) | perf | chain query creation instructions | + +### compiler-cli + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | +| [8a3f3a91cf6](https://github.com/angular/angular/commit/8a3f3a91cf6919f11c1583afeb71b65488f8cba4) | fix | expand type for native controls with a dynamic type | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------- | +| [aff8b248b35](https://github.com/angular/angular/commit/aff8b248b35931cbe7d644ad2594144b75064ca6) | feat | expose element on signal forms `Field` directive | +| [ebc5c2b083a](https://github.com/angular/angular/commit/ebc5c2b083a90fde6fced3521be434a414ef29dd) | feat | redo the signal forms metadata API | +| [9fe95665813](https://github.com/angular/angular/commit/9fe95665813e24544d00dcf463658067da745ac2) | fix | add signals for dirty, hidden, and pending states in custom controls | +| [14713d09923](https://github.com/angular/angular/commit/14713d09923b5315ac7c6f20acc06fc69ff0ed79) | fix | allow resetting with empty string | +| [b96f65a963f](https://github.com/angular/angular/commit/b96f65a963febab376f2a783cf4313a8b8140efe) | fix | memoize reads of child fields in signal forms ([#65802](https://github.com/angular/angular/pull/65802)) | +| [179b4cba67b](https://github.com/angular/angular/commit/179b4cba67b95563da27fd5f12dd2acc910a1a52) | fix | Reuse key in parent in compat structure | + +### upgrade + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | -------- | -------------------------- | +| [75fe8f8af94](https://github.com/angular/angular/commit/75fe8f8af9488bae6f7068b64d44500643c5d63f) | refactor | deprecate `VERSION` export | + + + + + +# 21.0.4 (2025-12-10) + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------------- | +| [f901cc9eb32](https://github.com/angular/angular/commit/f901cc9eb328bed74fd7f09607e54154254d4a97) | perf | chain query creation instructions | + +### compiler-cli + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | +| [65297c62011](https://github.com/angular/angular/commit/65297c62011ae353f8555738688a83a5fba5ea4e) | fix | expand type for native controls with a dynamic type | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------- | +| [f254ff4f2e0](https://github.com/angular/angular/commit/f254ff4f2e014064b4d6073341dec0c5a7a754bf) | feat | expose element on signal forms `Field` directive | +| [5880fbc73c6](https://github.com/angular/angular/commit/5880fbc73c6ac42976b3ada9803965bc20d047db) | feat | redo the signal forms metadata API | +| [55fc677cef4](https://github.com/angular/angular/commit/55fc677cef4409302bc474ff316d392097a034e7) | fix | add signals for dirty, hidden, and pending states in custom controls | +| [cbb10179c80](https://github.com/angular/angular/commit/cbb10179c8098f6a20b0bc365a492f14e4d2a51a) | fix | allow resetting with empty string | +| [bf1c12cd932](https://github.com/angular/angular/commit/bf1c12cd932028dc4bb50914c64bbb6d882b6ec1) | fix | memoize reads of child fields in signal forms ([#65802](https://github.com/angular/angular/pull/65802)) | +| [6d7475582f9](https://github.com/angular/angular/commit/6d7475582f95720b4487f663d339a18a25374481) | fix | Reuse key in parent in compat structure | + + + + + +# 21.1.0-next.1 (2025-12-03) + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------- | +| [1c6b0704fb](https://github.com/angular/angular/commit/1c6b0704fb63d051fab8acff84d076abfbc4893a) | fix | prevent XSS via SVG animation `attributeName` and MathML/SVG URLs | + +### compiler-cli + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | --------------------------------------------------------- | +| [e30e61b789](https://github.com/angular/angular/commit/e30e61b7893a9b04ada54ab184c24e652dcd114e) | fix | avoid allocating an object for signals in production mode | +| [6773d3b97d](https://github.com/angular/angular/commit/6773d3b97d07dcbf7760c0c12d9384fa60f7082e) | fix | check that field radio button values are strings | + +### core + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------ | +| [d8ab83ca82](https://github.com/angular/angular/commit/d8ab83ca82c36faacae9fc1a01e9eb9762e679fc) | fix | run animation queue in environment injector context | +| [886cf6c452](https://github.com/angular/angular/commit/886cf6c452abcf85b70058caeb080ff3a5c51cb9) | fix | unable to inject viewProviders when host directive with providers is present | +| [e6d5632a30](https://github.com/angular/angular/commit/e6d5632a3059a4920f19eb6b193ec7edb11645d4) | perf | tree shake unused dynamic `[field]` binding instructions ([#65599](https://github.com/angular/angular/pull/65599)) | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | -------------------------------------------------------------------------------------------- | +| [7d1e502345](https://github.com/angular/angular/commit/7d1e50234515185ee73c8bdd39f7af2a26635ca2) | feat | Allows transforms on `FormUiControl` signals | +| [cd7ae7e2ce](https://github.com/angular/angular/commit/cd7ae7e2ce4648edf62c047b3e453c8b32539d4a) | fix | support dynamic `[field]` bindings ([#65599](https://github.com/angular/angular/pull/65599)) | + +### http + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ---------------------------------------------------- | +| [0659d11c85](https://github.com/angular/angular/commit/0659d11c854c9c6b886a2e925afef98d70bb005f) | fix | enable XSRF protection for same-origin absolute URLs | + +### router + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | --------------------------------------------------- | +| [b74a0693f2](https://github.com/angular/angular/commit/b74a0693f2409812ad1ab02f19eb3fbe7ce1b83e) | fix | handle errors from view transition finished promise | + + + + + +# 21.0.3 (2025-12-03) + +### compiler-cli + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | --------------------------------------------------------- | +| [5a80a48e96](https://github.com/angular/angular/commit/5a80a48e962f72825050202198b32abbfee66714) | fix | avoid allocating an object for signals in production mode | +| [1f1856e897](https://github.com/angular/angular/commit/1f1856e897e0a10e2ca6d934c80fd69d1ac06210) | fix | check that field radio button values are strings | + +### core + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------ | +| [8c3304c766](https://github.com/angular/angular/commit/8c3304c766131b031b736ee3fe2ec9c9a42fbe07) | fix | run animation queue in environment injector context | +| [4bb085311e](https://github.com/angular/angular/commit/4bb085311e24966ef2dd673f23746988c449c7ff) | fix | unable to inject viewProviders when host directive with providers is present | +| [609699ae17](https://github.com/angular/angular/commit/609699ae1781a9160b0f474b7ebe0998221c0722) | perf | tree shake unused dynamic `[field]` binding instructions ([#65599](https://github.com/angular/angular/pull/65599)) | + +### forms + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | -------------------------------------------------------------------------------------------- | +| [6b4ab876e8](https://github.com/angular/angular/commit/6b4ab876e811b4e3a6f9617a2b379f62cf187403) | feat | Allows transforms on `FormUiControl` signals | +| [a5dbd4b382](https://github.com/angular/angular/commit/a5dbd4b382417fc111d6a622862a015c47027a41) | fix | support dynamic `[field]` bindings ([#65599](https://github.com/angular/angular/pull/65599)) | + +### http + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ---------------------------------------------------- | +| [20474d3f0f](https://github.com/angular/angular/commit/20474d3f0fd7c64071add6e84acf720627e5c19b) | fix | enable XSRF protection for same-origin absolute URLs | + +### router + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | --------------------------------------------------- | +| [48b89f9fbe](https://github.com/angular/angular/commit/48b89f9fbe16acff8b2f3f37853e745ed43d3a32) | fix | handle errors from view transition finished promise | + + + + + +# 21.0.2 (2025-12-01) + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------- | +| [78fd159b78](https://github.com/angular/angular/commit/78fd159b78d32cb8b94891e3fc6013076d7838af) | fix | prevent XSS via SVG animation `attributeName` and MathML/SVG URLs | + + + + + +# 20.3.15 (2025-12-01) + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------- | +| [d1ca8ae043](https://github.com/angular/angular/commit/d1ca8ae04390f050039fdb653a6147d75d48f81e) | fix | prevent XSS via SVG animation `attributeName` and MathML/SVG URLs | + + + + + +# 19.2.17 (2025-12-01) + +### compiler + +| Commit | Type | Description | +| ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------- | +| [7c42e2ebeb](https://github.com/angular/angular/commit/7c42e2ebebc135e9949a9e9a0295ef3ccf261b82) | fix | prevent XSS via SVG animation `attributeName` and MathML/SVG URLs | + + + # 19.2.16 (2025-11-26) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ff242268104..11cbd8a3e636 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,35 +69,52 @@ Unfortunately, we are not able to investigate / fix bugs without a minimal repro You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template. -### Submitting a Pull Request (PR) +### Contribution Quality -Before you submit your Pull Request (PR) consider the following guidelines: +We strongly value open source contribution and pull requests from community contributors. Please note that every pull request is reviewed and merged by an actual person on the team, which does take time and effort. That is time and effort that does take away from other valuable work. With that in mind we have an minimum set of expectations that are required of any community contribution pull request that is opened. 1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission. - You don't want to duplicate existing efforts. + - You don't want to duplicate existing efforts. +2. Be sure that an issue or pull request clearly describes the problem you're fixing, or documents the design for the feature you'd like to add. Issues require a _minimal_ reproduction. + +3. Discussing the design in an issue upfront helps to ensure that we're ready to accept your work. Pull requests are not the right place to do design work. + - When in doubt, open an issue first before doing any sort of speculative implementation work + +4. Ideally the PR should be tied to an issue, but this is not required + +5. The change should improve code quality (i.e. addressing a TODO) or should impact / improve a feature + +6. Micro optimizations will only be accepted if they are validated by an actual benchmark + +7. Do not open pull requests that are addressing feature requests that are not labeled as "help wanted" as they usually need additional design work before we could accept pull requests -2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. - Discussing the design upfront helps to ensure that we're ready to accept your work. +8. The change should be well tested -3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs. +If your pull request does not meet these minimum expectations, we may close your PR. Also, if your PR introduces a breaking change, it's possible the level of churn this breaking change causes may block our ability to move forward with it. We may close your PR in that situation, as well. Otherwise, we're excited to see your contributions and enthusiasm for Angular! + +### Submitting a Pull Request (PR) + +Before you submit your Pull Request (PR) consider the following guidelines: + +1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs. We cannot accept code without a signed CLA. Make sure you author all contributed Git commits with email address associated with your CLA signature. -4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the [angular/angular](https://github.com/angular/angular/fork) repo. +2. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the [angular/angular](https://github.com/angular/angular/fork) repo. -5. In your forked repository, make your changes in a new git branch: +3. In your forked repository, make your changes in a new git branch: ```shell git checkout -b my-fix-branch main ``` -6. Create your patch, **including appropriate test cases**. +4. Create your patch, **including appropriate test cases**. -7. Follow our [Coding Rules](#rules). +5. Follow our [Coding Rules](#rules). -8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass. +6. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass. -9. Commit your changes using a descriptive commit message that follows our [commit message conventions][commit-message-guidelines]. +7. Commit your changes using a descriptive commit message that follows our [commit message conventions][commit-message-guidelines]. Adherence to these conventions is necessary because release notes are automatically generated from these messages. ```shell @@ -106,13 +123,13 @@ Before you submit your Pull Request (PR) consider the following guidelines: Note: the optional commit `--all` command line option will automatically "add" and "rm" edited files. -10. Push your branch to GitHub: +8. Push your branch to GitHub: - ```shell - git push origin my-fix-branch - ``` + ```shell + git push origin my-fix-branch + ``` -11. In GitHub, send a pull request to `angular:main`. +9. In GitHub, send a pull request to `angular:main`. ### Reviewing a Pull Request diff --git a/MODULE.bazel b/MODULE.bazel index 51e8bbaa0ee3..32c62ccd433c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,26 +6,26 @@ module( bazel_dep(name = "rules_pkg", version = "1.1.0") bazel_dep(name = "rules_nodejs", version = "6.6.2") -bazel_dep(name = "aspect_rules_ts", version = "3.7.1") -bazel_dep(name = "aspect_rules_js", version = "2.8.2") -bazel_dep(name = "aspect_rules_esbuild", version = "0.24.0") -bazel_dep(name = "aspect_rules_jasmine", version = "2.0.0") +bazel_dep(name = "aspect_rules_ts", version = "3.8.1") +bazel_dep(name = "aspect_rules_js", version = "2.8.3") +bazel_dep(name = "aspect_rules_esbuild", version = "0.25.0") +bazel_dep(name = "aspect_rules_jasmine", version = "2.0.2") bazel_dep(name = "aspect_rules_rollup", version = "2.0.1") -bazel_dep(name = "bazel_skylib", version = "1.8.2") -bazel_dep(name = "aspect_bazel_lib", version = "2.21.2") +bazel_dep(name = "bazel_skylib", version = "1.9.0") +bazel_dep(name = "aspect_bazel_lib", version = "2.22.0") bazel_dep(name = "tar.bzl", version = "0.7.0") bazel_dep(name = "yq.bzl", version = "0.3.2") bazel_dep(name = "rules_angular") git_override( module_name = "rules_angular", - commit = "9b751f826628cab386d1e8ae9c1fa766dbc6a495", + commit = "7133b97252508f8528e5c5818a9a73cacc2e2a0e", remote = "https://github.com/devversion/rules_angular.git", ) bazel_dep(name = "devinfra") git_override( module_name = "devinfra", - commit = "95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae", + commit = "dd5658bd720370542913e23b21d80450bac94b60", remote = "https://github.com/angular/dev-infra.git", ) @@ -39,7 +39,7 @@ git_override( bazel_dep(name = "rules_browsers") git_override( module_name = "rules_browsers", - commit = "f066f614451374721fac787fb1f0dbbd818d50d4", + commit = "8ef3e996d5fc040a35770f860987d8b9cad8ef3d", remote = "https://github.com/devversion/rules_browsers.git", ) @@ -47,7 +47,7 @@ yq = use_extension("@yq.bzl//yq:extensions.bzl", "yq") use_repo(yq, "yq_toolchains") node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") -node.toolchain(node_version = "20.19.0") +node.toolchain(node_version = "22.21.1") use_repo(node, "nodejs_toolchains") use_repo(node, "nodejs_darwin_amd64") use_repo(node, "nodejs_darwin_arm64") @@ -101,7 +101,6 @@ npm.npm_translate_lock( ], npmrc = "//:.npmrc", pnpm_lock = "//:pnpm-lock.yaml", - verify_node_modules_ignored = "//:.bazelignore", ) use_repo(npm, "npm") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 38d365db29b4..c82df391c553 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,5 +1,5 @@ { - "lockFileVersion": 13, + "lockFileVersion": 24, "registryFileHashes": { "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", @@ -10,37 +10,36 @@ "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", - "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", - "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.0.0/MODULE.bazel": "e118477db5c49419a88d78ebc7a2c2cea9d49600fe0f490c1903324a2c16ecd9", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.11.0/MODULE.bazel": "cb1ba9f9999ed0bc08600c221f532c1ddd8d217686b32ba7d45b0713b5131452", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.17.1/MODULE.bazel": "9b027af55f619c7c444cead71061578fab6587e5e1303fa4ed61d49d2b1a7262", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", - "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/MODULE.bazel": "276347663a25b0d5bd6cad869252bea3e160c4d980e764b15f3bae7f80b30624", - "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/source.json": "f42051fa42629f0e59b7ac2adf0a55749144b11f1efcd8c697f0ee247181e526", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.0/MODULE.bazel": "7fe0191f047d4fe4a4a46c1107e2350cbb58a8fc2e10913aa4322d3190dec0bf", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.0/source.json": "369df5b7f2eae82f200fff95cf1425f90dee90a0d0948122060b48150ff0e224", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.9.3/MODULE.bazel": "66baf724dbae7aff4787bf2245cc188d50cb08e07789769730151c0943587c14", - "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.24.0/MODULE.bazel": "15d19e46ec74e9e49ddf3c335e7a91b0657571654b0960bdcd10b771eeb4f7cb", - "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.24.0/source.json": "6cc8c0ba6c623527e383acfe4ee73f290eeead2431093668db3b7a579a948deb", - "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.0/MODULE.bazel": "071d1952527721bf8b180e1299def24edaece9d7466e31a311981640da82c6be", - "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.0/source.json": "45fa9603cdfe100575a12d8b65fa425fe8713dd8c9f0cdf802168b670bc0e299", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.0/MODULE.bazel": "5fef5ec709c837312823f9bcf0f276661e2cb48ad52f17c4e01176bbf1e9bf58", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.0/source.json": "5e42968c6d23ab8bd95c02634b16864d866334347827cb6a8425b86c11cc4363", + "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/MODULE.bazel": "45f054400ff242c4433f6d7f20f6123a9a72739cb7a1f44247d738db1644f46c", + "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/source.json": "3ed399a5654259a822448f9cdbf21f6c738f16ccd7f89249c7507e374cbdd1e3", "https://bcr.bazel.build/modules/aspect_rules_js/2.0.0/MODULE.bazel": "b45b507574aa60a92796e3e13c195cd5744b3b8aff516a9c0cb5ae6a048161c5", "https://bcr.bazel.build/modules/aspect_rules_js/2.4.2/MODULE.bazel": "0d01db38b96d25df7ed952a5e96eac4b3802723d146961974bf020f6dd07591d", "https://bcr.bazel.build/modules/aspect_rules_js/2.6.2/MODULE.bazel": "ed2a871f4ab8fbde0cab67c425745069d84ea64b64313fa1a2954017326511f5", - "https://bcr.bazel.build/modules/aspect_rules_js/2.8.2/MODULE.bazel": "76526405d6a65dae45db16b8b4619b502063ac573d2a2ae0a90fddc7d3247288", - "https://bcr.bazel.build/modules/aspect_rules_js/2.8.2/source.json": "a03164e3f59a05d9a6d205a477ea49ba8ee141ab764a9f38b43e6302eb4fa2b9", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/MODULE.bazel": "807ce5f624124a8bc586c743394d174e85f0f9c6c4e0e2410b4088aebe790ac8", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/source.json": "c35cb4e04f61a09c17f8c569894b80de884b1e3dee2d33829704786e3f778037", "https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/MODULE.bazel": "296e3a053658c2af989ba9bd62a205e6d1fa84bdd6dd5249196546e6b84770ec", "https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/source.json": "2fe8ac1ccb4de74bf884761e070010280b272d94e3997205b361b91c75409726", "https://bcr.bazel.build/modules/aspect_rules_ts/3.6.3/MODULE.bazel": "d09db394970f076176ce7bab5b5fa7f0d560fd4f30b8432ea5e2c2570505b130", "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.0/MODULE.bazel": "5aace216caf88638950ef061245d23c36f57c8359e56e97f02a36f70bb09c50f", - "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.1/MODULE.bazel": "cbed416847e2c46c4c0fe275e3a3c8e302d236d0fb04a094e9af82d14e7c5040", - "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.1/source.json": "7914a860fdf6ac399a3142fee2579184f0810fe0b7dee2eb9216ab72e6d8864e", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.1/MODULE.bazel": "796622c65ae3008374fc2d77c32ddb4ef6da9fe891826ce648f70033a48b3667", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.1/source.json": "a7c4f332f5c21f4e63d073f8dda40bf278d5307499fb307b35058dba558f417a", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.3/MODULE.bazel": "20f53b145f40957a51077ae90b37b7ce83582a1daf9350349f0f86179e19dd0d", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.6/MODULE.bazel": "cafb8781ad591bc57cc765dca5fefab08cf9f65af363d162b79d49205c7f8af7", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679", - "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/source.json": "786cbc49377fb6bf4859aec5b1c61f8fc26b08e9fdb929e2dde2e1e2a406bd24", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "37c764292861c2f70314efa9846bb6dbb44fc0308903b3285da6528305450183", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/source.json": "605086bbc197743a0d360f7ddc550a1d4dfa0441bc807236e17170f636153348", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", @@ -48,11 +47,12 @@ "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", - "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", "https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e", "https://bcr.bazel.build/modules/bazel_features/1.34.0/source.json": "dfa5c4b01110313153b484a735764d247fee5624bbab63d25289e43b151a657a", "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "407729e232f611c3270005b016b437005daa7b1505826798ea584169a476e878", "https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d", "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751", @@ -70,7 +70,8 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", - "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", @@ -94,7 +95,6 @@ "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", @@ -102,9 +102,9 @@ "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", - "https://bcr.bazel.build/modules/protobuf/29.0-rc3/source.json": "c16a6488fb279ef578da7098e605082d72ed85fc8d843eaae81e7d27d0f4625d", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", @@ -113,8 +113,8 @@ "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", - "https://bcr.bazel.build/modules/rules_cc/0.0.11/MODULE.bazel": "9f249c5624a4788067b96b8b896be10c7e8b4375dc46f6d8e1e51100113e0992", "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", @@ -137,10 +137,10 @@ "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", - "https://bcr.bazel.build/modules/rules_java/8.5.1/source.json": "db1a77d81b059e0f84985db67a22f3f579a529a86b7997605be3d214a0abe38e", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", @@ -172,12 +172,12 @@ "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", "https://bcr.bazel.build/modules/rules_python/1.0.0/source.json": "b0162a65c6312e45e7912e39abd1a7f8856c2c7e41ecc9b6dc688a6f6400a917", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", @@ -202,24 +202,22 @@ "https://bcr.bazel.build/modules/yq.bzl/0.3.2/MODULE.bazel": "0384efa70e8033d842ea73aa4b7199fa099709e236a7264345c03937166670b6", "https://bcr.bazel.build/modules/yq.bzl/0.3.2/source.json": "c4ec3e192477e154f08769e29d69e8fd36e8a4f0f623997f3e1f6f7d328f7d7d", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" }, "selectedYankedVersions": {}, "moduleExtensions": { - "@@aspect_rules_esbuild~//esbuild:extensions.bzl%esbuild": { + "@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": { "general": { - "bzlTransitiveDigest": "Kp1ElwnSsU9URW4hnpM9wzhITxGUNAp4tu7dOwA82Qo=", - "usagesDigest": "w3kRc6iou9hC6M+vwECvdfk0P8nsVNjHSl4Ftru44zU=", + "bzlTransitiveDigest": "0aod3RK04ALA/OKTExzd7QqyeqcC4O2GWuDwUxhQHp4=", + "usagesDigest": "ToTaCONCN/E05krnHXLM1kpV1zrHNxHrGpUip973II4=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "esbuild_darwin-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -227,8 +225,7 @@ } }, "esbuild_darwin-arm64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -236,8 +233,7 @@ } }, "esbuild_linux-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -245,8 +241,7 @@ } }, "esbuild_linux-arm64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -254,8 +249,7 @@ } }, "esbuild_win32-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -263,16 +257,14 @@ } }, "esbuild_toolchains": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild/private:toolchains_repo.bzl", - "ruleClassName": "toolchains_repo", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild/private:toolchains_repo.bzl%toolchains_repo", "attributes": { "esbuild_version": "0.19.9", "user_repository_name": "esbuild" } }, "npm__esbuild_0.19.9": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_rule", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", "attributes": { "package": "esbuild", "version": "0.19.9", @@ -299,8 +291,7 @@ } }, "npm__esbuild_0.19.9__links": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_links", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", "attributes": { "package": "esbuild", "version": "0.19.9", @@ -326,104 +317,103 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_tools", "bazel_tools" ], [ - "bazel_lib~", + "bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "bazel_lib~", + "bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "tar.bzl~", + "tar.bzl+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ] ] } }, - "@@aspect_rules_js~//npm:extensions.bzl%pnpm": { + "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { "general": { - "bzlTransitiveDigest": "b2732vQWZpf2g1U6Rf2M0kXVkyN5QVnEn69W2+zHZPQ=", - "usagesDigest": "dsPwWu5gO0edyjNQdbC1Jf0bIJqTqhEueQ6v14GWzGU=", + "bzlTransitiveDigest": "tQ+7EwLfQwqi/T4v5/N3NNHTmP6Wu/FqXxRDndEB2OU=", + "usagesDigest": "3ECQVM1bdy/C4w7vxglaG1gt0S98v0T14gP5wNiDgwY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "pnpm": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_rule", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", "attributes": { "package": "pnpm", "version": "10.16.1", @@ -450,8 +440,7 @@ } }, "pnpm__links": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_links", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", "attributes": { "package": "pnpm", "version": "10.16.1", @@ -477,106 +466,105 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_features", - "bazel_features~" + "bazel_features+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_tools", "bazel_tools" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_globals", - "bazel_features~~version_extension~bazel_features_globals" + "bazel_features++version_extension+bazel_features_globals" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_version", - "bazel_features~~version_extension~bazel_features_version" + "bazel_features++version_extension+bazel_features_version" ], [ - "bazel_lib~", + "bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "bazel_lib~", + "bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "tar.bzl~", + "tar.bzl+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ] ] } }, - "@@aspect_rules_ts~//ts:extensions.bzl%ext": { + "@@aspect_rules_ts+//ts:extensions.bzl%ext": { "general": { - "bzlTransitiveDigest": "pEu5+6q07zdUqscbVkhWTNLGT9OGRpnFCF4OGHv1n1k=", - "usagesDigest": "vXjWlVY215W1kYADgJxzHrW6dJgUiQbgkwqYp5ENPLc=", + "bzlTransitiveDigest": "7k3bewVApw4Kc6Rpho1Rrs1nrW/5jphUA5Mh1iHE2U4=", + "usagesDigest": "BIPOUwqzuuYqLOw/0p3h5GwHIDeBVXvNcxNoG5YlsHQ=", "recordedFileInputs": { - "@@rules_browsers~//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5" + "@@rules_browsers+//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5" }, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "angular_npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.3", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", @@ -586,8 +574,7 @@ } }, "rules_angular_npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.2", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", @@ -597,8 +584,7 @@ } }, "npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.3", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", @@ -608,11 +594,10 @@ } }, "npm_rules_browsers_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "", - "version_from": "@@rules_browsers~//:package.json", + "version_from": "@@rules_browsers+//:package.json", "integrity": "", "urls": [ "https://registry.npmjs.org/typescript/-/typescript-{}.tgz" @@ -622,73 +607,71 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_rules_ts~", + "aspect_rules_ts+", "aspect_rules_ts", - "aspect_rules_ts~" + "aspect_rules_ts+" ], [ - "aspect_rules_ts~", + "aspect_rules_ts+", "bazel_tools", "bazel_tools" ] ] } }, - "@@aspect_tools_telemetry~//:extension.bzl%telemetry": { + "@@aspect_tools_telemetry+//:extension.bzl%telemetry": { "general": { - "bzlTransitiveDigest": "gA7tPEdJXhskzPIEUxjX9IdDrM6+WjfbgXJ8Ez47umk=", - "usagesDigest": "Z00juRvw/tDmEBGMQcL1uZmyWji5K7VsqVIZFWYXAIA=", + "bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=", + "usagesDigest": "c8kIEi+rX5/1OpfMZW0mHrM7sAvw+nu7pOep2xSjFb4=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "aspect_tools_telemetry_report": { - "bzlFile": "@@aspect_tools_telemetry~//:extension.bzl", - "ruleClassName": "tel_repository", + "repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository", "attributes": { "deps": { - "aspect_rules_ts": "3.7.1", - "aspect_rules_js": "2.8.2", - "aspect_rules_esbuild": "0.24.0", - "aspect_tools_telemetry": "0.2.8" + "aspect_rules_ts": "3.8.1", + "aspect_rules_js": "2.8.3", + "aspect_rules_esbuild": "0.25.0", + "aspect_rules_jasmine": "2.0.2", + "aspect_tools_telemetry": "0.3.3" } } } }, "recordedRepoMappingEntries": [ [ - "aspect_tools_telemetry~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_tools_telemetry+", + "bazel_lib", + "bazel_lib+" ], [ - "aspect_tools_telemetry~", + "aspect_tools_telemetry+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ] ] } }, - "@@pybind11_bazel~//:python_configure.bzl%extension": { + "@@pybind11_bazel+//:python_configure.bzl%extension": { "general": { - "bzlTransitiveDigest": "dFd3A3f+jPCss+EDKMp/jxjcUhfMku130eT1KGxSCwA=", - "usagesDigest": "gNvOHVcAlwgDsNXD0amkv2CC96mnaCThPQoE44y8K+w=", + "bzlTransitiveDigest": "c9ZWWeXeu6bctL4/SsY2otFWyeFN0JJ20+ymGyJZtWk=", + "usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=", "recordedFileInputs": { - "@@pybind11_bazel~//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" + "@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" }, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "local_config_python": { - "bzlFile": "@@pybind11_bazel~//:python_configure.bzl", - "ruleClassName": "python_configure", + "repoRuleId": "@@pybind11_bazel+//:python_configure.bzl%python_configure", "attributes": {} }, "pybind11": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { - "build_file": "@@pybind11_bazel~//:pybind11.BUILD", + "build_file": "@@pybind11_bazel+//:pybind11.BUILD", "strip_prefix": "pybind11-2.11.1", "urls": [ "https://github.com/pybind/pybind11/archive/v2.11.1.zip" @@ -698,52 +681,49 @@ }, "recordedRepoMappingEntries": [ [ - "pybind11_bazel~", + "pybind11_bazel+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_angular~//setup:extensions.bzl%rules_angular": { + "@@rules_angular+//setup:extensions.bzl%rules_angular": { "general": { "bzlTransitiveDigest": "fkaH7HMicL3g7/NDaFzlq39kcLopMyQ3KdbDn+5CRzA=", - "usagesDigest": "c/HgGZ21I+QxW0XkaMGJK0N3nV5rdEiNEcPkToa4i9M=", + "usagesDigest": "RWY3uqO/f0lDK8WZtS5QwE7S7IrkU3PrHr2/Vje4M3s=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "rules_angular_configurable_deps": { - "bzlFile": "@@rules_angular~//setup:repositories.bzl", - "ruleClassName": "configurable_deps_repo", + "repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo", "attributes": { - "angular_compiler_cli": "@@rules_angular~//:node_modules/@angular/compiler-cli", - "typescript": "@@rules_angular~//:node_modules/typescript-local" + "angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli", + "typescript": "@@rules_angular+//:node_modules/typescript-local" } }, "dev_infra_rules_angular_configurable_deps": { - "bzlFile": "@@rules_angular~//setup:repositories.bzl", - "ruleClassName": "configurable_deps_repo", + "repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo", "attributes": { - "angular_compiler_cli": "@@rules_angular~//:node_modules/@angular/compiler-cli", - "typescript": "@@rules_angular~//:node_modules/typescript" + "angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli", + "typescript": "@@rules_angular+//:node_modules/typescript" } } }, "recordedRepoMappingEntries": [] } }, - "@@rules_browsers~//browsers:extensions.bzl%browsers": { + "@@rules_browsers+//browsers:extensions.bzl%browsers": { "general": { "bzlTransitiveDigest": "ljZlVgWkQJnI6EvlHVfYit2EttUE52gDTbvmota5YO8=", - "usagesDigest": "1PlExi+b77pSr2tAxFCVbpCtFoA7oixHabaL3dmas4Y=", + "usagesDigest": "FS7q5WaIwg3KirS3njhuPFkTIBYvDaTInVGrlzu0XL8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "rules_browsers_chrome_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "1419fa328bd7ea2697f26412ec693867516e4ef23c32eb13143a0b0b179b604b", "urls": [ @@ -761,8 +741,7 @@ } }, "rules_browsers_chrome_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "792cbf9b77219b4476e41c49647bcd15e55f0988002fa1e4e6a720eb430c7eda", "urls": [ @@ -780,8 +759,7 @@ } }, "rules_browsers_chrome_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "f0c1917769775e826dfa69936381d0d95b06fe67cf631ecd842380d5de0e4c7f", "urls": [ @@ -799,8 +777,7 @@ } }, "rules_browsers_chrome_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "6ce0f20dd743a804890f45f5349370e1aa7cd3ac3482c04686fcff5fafd01bb3", "urls": [ @@ -818,8 +795,7 @@ } }, "rules_browsers_chromedriver_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "baf4bf9d22881265487732f17d35a49e9aadd0837aa5c1c1eea520c8aa24a97f", "urls": [ @@ -835,8 +811,7 @@ } }, "rules_browsers_chromedriver_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "87560768d5aa203b37c0a1b8459a35b05e4ece54afee2df530f3bc33de4f63c5", "urls": [ @@ -852,8 +827,7 @@ } }, "rules_browsers_chromedriver_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "99821795fa7c87eb92fb15248e23b237c83f397486d22ad9a10771622c36a5a0", "urls": [ @@ -869,8 +843,7 @@ } }, "rules_browsers_chromedriver_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "6e180e234a710c3cbf69566f64a662ed85473db6ae82275fd359f80ab288df99", "urls": [ @@ -886,8 +859,7 @@ } }, "rules_browsers_firefox_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "00fb922cda6bab971e02bcbfb77923b0a234388ed7d77c23506ca0a1a61d4a86", "urls": [ @@ -903,8 +875,7 @@ } }, "rules_browsers_firefox_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "1c4556480deac8424049f3081a6de1e2c6de619bab3e8ce53e5a497b8d6d919e", "urls": [ @@ -920,8 +891,7 @@ } }, "rules_browsers_firefox_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "1c4556480deac8424049f3081a6de1e2c6de619bab3e8ce53e5a497b8d6d919e", "urls": [ @@ -937,8 +907,7 @@ } }, "rules_browsers_firefox_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { "sha256": "4b0345c113242653d923b369fcbd48e3089c57658f8c1542f887c8a375d50306", "urls": [ @@ -957,17 +926,16 @@ "recordedRepoMappingEntries": [] } }, - "@@rules_fuzzing~//fuzzing/private:extensions.bzl%non_module_dependencies": { + "@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "VMhyxXtdJvrNlLts7afAymA+pOatXuh5kLdxzVAZ/04=", - "usagesDigest": "YnIrdgwnf3iCLfChsltBdZ7yOJh706lpa2vww/i2pDI=", + "bzlTransitiveDigest": "WHRlQQnxW7e7XMRBhq7SARkDarLDOAbg6iLaJpk5QYM=", + "usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "platforms": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "urls": [ "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", @@ -977,8 +945,7 @@ } }, "rules_python": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", "strip_prefix": "rules_python-0.28.0", @@ -986,8 +953,7 @@ } }, "bazel_skylib": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", "urls": [ @@ -997,8 +963,7 @@ } }, "com_google_absl": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "urls": [ "https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip" @@ -1008,31 +973,27 @@ } }, "rules_fuzzing_oss_fuzz": { - "bzlFile": "@@rules_fuzzing~//fuzzing/private/oss_fuzz:repository.bzl", - "ruleClassName": "oss_fuzz_repository", + "repoRuleId": "@@rules_fuzzing+//fuzzing/private/oss_fuzz:repository.bzl%oss_fuzz_repository", "attributes": {} }, "honggfuzz": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { - "build_file": "@@rules_fuzzing~//:honggfuzz.BUILD", + "build_file": "@@rules_fuzzing+//:honggfuzz.BUILD", "sha256": "6b18ba13bc1f36b7b950c72d80f19ea67fbadc0ac0bb297ec89ad91f2eaa423e", "url": "https://github.com/google/honggfuzz/archive/2.5.zip", "strip_prefix": "honggfuzz-2.5" } }, "rules_fuzzing_jazzer": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_jar", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", "attributes": { "sha256": "ee6feb569d88962d59cb59e8a31eb9d007c82683f3ebc64955fd5b96f277eec2", "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer/0.20.1/jazzer-0.20.1.jar" } }, "rules_fuzzing_jazzer_api": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_jar", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", "attributes": { "sha256": "f5a60242bc408f7fa20fccf10d6c5c5ea1fcb3c6f44642fec5af88373ae7aa1b", "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer-api/0.20.1/jazzer-api-0.20.1.jar" @@ -1041,47 +1002,23 @@ }, "recordedRepoMappingEntries": [ [ - "rules_fuzzing~", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, - "@@rules_java~//java:rules_java_deps.bzl%compatibility_proxy": { - "general": { - "bzlTransitiveDigest": "C4xqrMy1wN4iuTN6Z2eCm94S5XingHhD6uwrIXvCxVI=", - "usagesDigest": "pwHZ+26iLgQdwvdZeA5wnAjKnNI3y6XO2VbhOTeo5h8=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "compatibility_proxy": { - "bzlFile": "@@rules_java~//java:rules_java_deps.bzl", - "ruleClassName": "_compatibility_proxy_repo_rule", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "rules_java~", + "rules_fuzzing+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_kotlin~//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "eecmTsmdIQveoA97hPtH3/Ej/kugbdCI24bhXIXaly8=", - "usagesDigest": "aJF6fLy82rR95Ff5CZPAqxNoFgOMLMN5ImfBS0nhnkg=", + "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "com_github_jetbrains_kotlin_git": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:compiler.bzl", - "ruleClassName": "kotlin_compiler_git_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", "attributes": { "urls": [ "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" @@ -1090,16 +1027,14 @@ } }, "com_github_jetbrains_kotlin": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:compiler.bzl", - "ruleClassName": "kotlin_capabilities_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", "attributes": { "git_repository_name": "com_github_jetbrains_kotlin_git", "compiler_version": "1.9.23" } }, "com_github_google_ksp": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:ksp.bzl", - "ruleClassName": "ksp_compiler_plugin_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", "attributes": { "urls": [ "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" @@ -1109,8 +1044,7 @@ } }, "com_github_pinterest_ktlint": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", "attributes": { "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", "urls": [ @@ -1120,8 +1054,7 @@ } }, "rules_android": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", "strip_prefix": "rules_android-0.1.1", @@ -1133,150 +1066,139 @@ }, "recordedRepoMappingEntries": [ [ - "rules_kotlin~", + "rules_kotlin+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_nodejs~//nodejs:extensions.bzl%node": { + "@@rules_nodejs+//nodejs:extensions.bzl%node": { "general": { "bzlTransitiveDigest": "NwcLXHrbh2hoorA/Ybmcpjxsn/6avQmewDglodkDrgo=", - "usagesDigest": "PNFNSvSa5+EiwNAB+piNanzyTBfBM7qnCsl+zcUnjb8=", + "usagesDigest": "eycVhpa7dyjfsYiyP69uXfJ7I60zQlL3Bd0gcVPq8+8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "nodejs_linux_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "linux_amd64" } }, "nodejs_linux_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "linux_arm64" } }, "nodejs_linux_s390x": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "linux_s390x" } }, "nodejs_linux_ppc64le": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "linux_ppc64le" } }, "nodejs_darwin_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "darwin_amd64" } }, "nodejs_darwin_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "darwin_arm64" } }, "nodejs_windows_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "windows_amd64" } }, "nodejs_windows_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "20.19.0", + "node_version": "22.21.1", "include_headers": false, "platform": "windows_arm64" } }, "nodejs": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "nodejs" } }, "nodejs_host": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "nodejs" } }, "nodejs_toolchains": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_toolchains_repo.bzl", - "ruleClassName": "nodejs_toolchains_repo", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", "attributes": { "user_node_repository_name": "nodejs" } @@ -1285,16 +1207,16 @@ "recordedRepoMappingEntries": [] } }, - "@@rules_python~//python/extensions:pip.bzl%pip": { + "@@rules_python+//python/extensions:pip.bzl%pip": { "general": { - "bzlTransitiveDigest": "VKf3JZIRvp7gyc5Q9pSqri7bmB3079s5o6Yg7IaUHZI=", - "usagesDigest": "MKs5B778/fEkKhBaxuBt3oCCW+wPRuh2AxtITF8AMSU=", + "bzlTransitiveDigest": "39J6fxZx6VyebAMZs6LDsQSGw91SROMECqQx77bSJqE=", + "usagesDigest": "/9NP3RV6/DWuNdYAsIU/8UCgCX0TdPUJr0X6O+0lrtk=", "recordedFileInputs": { - "@@rules_python~//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d", - "@@rules_fuzzing~//fuzzing/requirements.txt": "ab04664be026b632a0d2a2446c4f65982b7654f5b6851d2f9d399a19b7242a5b", - "@@rules_python~//tools/publish/requirements_windows.txt": "7673adc71dc1a81d3661b90924d7a7c0fc998cd508b3cb4174337cef3f2de556", - "@@protobuf~//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5", - "@@rules_python~//tools/publish/requirements_darwin.txt": "2994136eab7e57b083c3de76faf46f70fad130bc8e7360a7fed2b288b69e79dc" + "@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5", + "@@rules_fuzzing+//fuzzing/requirements.txt": "ab04664be026b632a0d2a2446c4f65982b7654f5b6851d2f9d399a19b7242a5b", + "@@rules_python+//tools/publish/requirements_darwin.txt": "2994136eab7e57b083c3de76faf46f70fad130bc8e7360a7fed2b288b69e79dc", + "@@rules_python+//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d", + "@@rules_python+//tools/publish/requirements_windows.txt": "7673adc71dc1a81d3661b90924d7a7c0fc998cd508b3cb4174337cef3f2de556" }, "recordedDirentsInputs": {}, "envVariables": { @@ -1303,238 +1225,217 @@ }, "generatedRepoSpecs": { "pip_deps_310_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "pip_deps_310", "requirement": "numpy<=1.26.1" } }, "pip_deps_310_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "pip_deps_310", "requirement": "setuptools<=70.3.0" } }, "pip_deps_311_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "pip_deps_311", "requirement": "numpy<=1.26.1" } }, "pip_deps_311_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "pip_deps_311", "requirement": "setuptools<=70.3.0" } }, "pip_deps_312_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "pip_deps_312", "requirement": "numpy<=1.26.1" } }, "pip_deps_312_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "pip_deps_312", "requirement": "setuptools<=70.3.0" } }, "pip_deps_38_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "pip_deps_38", "requirement": "numpy<=1.26.1" } }, "pip_deps_38_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "pip_deps_38", "requirement": "setuptools<=70.3.0" } }, "pip_deps_39_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "pip_deps_39", "requirement": "numpy<=1.26.1" } }, "pip_deps_39_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "pip_deps_39", "requirement": "setuptools<=70.3.0" } }, "rules_fuzzing_py_deps_310_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "rules_fuzzing_py_deps_310", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_310_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "rules_fuzzing_py_deps_310", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_311_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_fuzzing_py_deps_311", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_311_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_fuzzing_py_deps_311", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_312_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "rules_fuzzing_py_deps_312", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_312_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "rules_fuzzing_py_deps_312", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_38_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "rules_fuzzing_py_deps_38", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_38_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "rules_fuzzing_py_deps_38", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_39_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "rules_fuzzing_py_deps_39", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_39_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "rules_fuzzing_py_deps_39", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_python_publish_deps_311_backports_tarfile_py3_none_any_77e284d7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1548,7 +1449,7 @@ "cp311_windows_x86_64" ], "filename": "backports.tarfile-1.2.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "backports-tarfile==1.2.0", "sha256": "77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", @@ -1558,8 +1459,7 @@ } }, "rules_python_publish_deps_311_backports_tarfile_sdist_d75e02c2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1577,7 +1477,7 @@ "https://pypi.org/simple" ], "filename": "backports_tarfile-1.2.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "backports-tarfile==1.2.0", "sha256": "d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", @@ -1587,8 +1487,7 @@ } }, "rules_python_publish_deps_311_certifi_py3_none_any_922820b5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1602,7 +1501,7 @@ "cp311_windows_x86_64" ], "filename": "certifi-2024.8.30-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "certifi==2024.8.30", "sha256": "922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", @@ -1612,8 +1511,7 @@ } }, "rules_python_publish_deps_311_certifi_sdist_bec941d2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1631,7 +1529,7 @@ "https://pypi.org/simple" ], "filename": "certifi-2024.8.30.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "certifi==2024.8.30", "sha256": "bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", @@ -1641,8 +1539,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_a1ed2dd2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1653,7 +1550,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", @@ -1663,8 +1560,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_ppc64le_46bf4316": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1675,7 +1571,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", @@ -1685,8 +1581,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_s390x_a24ed04c": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1697,7 +1592,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", @@ -1707,8 +1602,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_610faea7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1719,7 +1613,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", @@ -1729,8 +1623,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_aarch64_a9b15d49": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1741,7 +1634,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", @@ -1751,8 +1644,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_fc48c783": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1763,7 +1655,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", @@ -1773,8 +1665,7 @@ } }, "rules_python_publish_deps_311_cffi_sdist_1c39c601": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1789,7 +1680,7 @@ "https://pypi.org/simple" ], "filename": "cffi-1.17.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", @@ -1799,8 +1690,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_0d99dd8f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1814,7 +1704,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", @@ -1824,8 +1714,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_c57516e5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1839,7 +1728,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", @@ -1849,8 +1738,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_6dba5d19": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1864,7 +1752,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", @@ -1874,8 +1762,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_bf4475b8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1889,7 +1776,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", @@ -1899,8 +1786,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_ppc64le_ce031db0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1914,7 +1800,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", @@ -1924,8 +1810,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8ff4e7cd": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1939,7 +1824,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", @@ -1949,8 +1834,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_3710a975": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1964,7 +1848,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", @@ -1974,8 +1858,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_aarch64_47334db7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1989,7 +1872,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", @@ -1999,8 +1882,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_ppc64le_f1a2f519": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2014,7 +1896,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", @@ -2024,8 +1906,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_s390x_63bc5c4a": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2039,7 +1920,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", @@ -2049,8 +1930,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_x86_64_bcb4f8ea": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2064,7 +1944,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", @@ -2074,8 +1954,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_cee4373f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2089,7 +1968,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", @@ -2099,8 +1978,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_py3_none_any_fe9f97fe": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2114,7 +1992,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", @@ -2124,8 +2002,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_sdist_223217c3": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2143,7 +2020,7 @@ "https://pypi.org/simple" ], "filename": "charset_normalizer-3.4.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", @@ -2153,8 +2030,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_846da004": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2165,7 +2041,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", @@ -2175,8 +2051,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_0f996e72": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2187,7 +2062,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", @@ -2197,8 +2072,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_f7b178f1": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2209,7 +2083,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", @@ -2219,8 +2093,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_c2e6fc39": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2231,7 +2104,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", @@ -2241,8 +2114,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_e1be4655": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2253,7 +2125,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", @@ -2263,8 +2135,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_df6b6c6d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2275,7 +2146,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", @@ -2285,8 +2156,7 @@ } }, "rules_python_publish_deps_311_cryptography_sdist_315b9001": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2301,7 +2171,7 @@ "https://pypi.org/simple" ], "filename": "cryptography-43.0.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", @@ -2311,8 +2181,7 @@ } }, "rules_python_publish_deps_311_docutils_py3_none_any_dafca5b9": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2326,7 +2195,7 @@ "cp311_windows_x86_64" ], "filename": "docutils-0.21.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "docutils==0.21.2", "sha256": "dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", @@ -2336,8 +2205,7 @@ } }, "rules_python_publish_deps_311_docutils_sdist_3a6b1873": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2355,7 +2223,7 @@ "https://pypi.org/simple" ], "filename": "docutils-0.21.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "docutils==0.21.2", "sha256": "3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", @@ -2365,8 +2233,7 @@ } }, "rules_python_publish_deps_311_idna_py3_none_any_946d195a": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2380,7 +2247,7 @@ "cp311_windows_x86_64" ], "filename": "idna-3.10-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "idna==3.10", "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", @@ -2390,8 +2257,7 @@ } }, "rules_python_publish_deps_311_idna_sdist_12f65c9b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2409,7 +2275,7 @@ "https://pypi.org/simple" ], "filename": "idna-3.10.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "idna==3.10", "sha256": "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", @@ -2419,8 +2285,7 @@ } }, "rules_python_publish_deps_311_importlib_metadata_py3_none_any_45e54197": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2434,7 +2299,7 @@ "cp311_windows_x86_64" ], "filename": "importlib_metadata-8.5.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "importlib-metadata==8.5.0", "sha256": "45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", @@ -2444,8 +2309,7 @@ } }, "rules_python_publish_deps_311_importlib_metadata_sdist_71522656": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2463,7 +2327,7 @@ "https://pypi.org/simple" ], "filename": "importlib_metadata-8.5.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "importlib-metadata==8.5.0", "sha256": "71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", @@ -2473,8 +2337,7 @@ } }, "rules_python_publish_deps_311_jaraco_classes_py3_none_any_f662826b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2488,7 +2351,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.classes-3.4.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-classes==3.4.0", "sha256": "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", @@ -2498,8 +2361,7 @@ } }, "rules_python_publish_deps_311_jaraco_classes_sdist_47a024b5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2517,7 +2379,7 @@ "https://pypi.org/simple" ], "filename": "jaraco.classes-3.4.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-classes==3.4.0", "sha256": "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", @@ -2527,8 +2389,7 @@ } }, "rules_python_publish_deps_311_jaraco_context_py3_none_any_f797fc48": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2542,7 +2403,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.context-6.0.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-context==6.0.1", "sha256": "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", @@ -2552,8 +2413,7 @@ } }, "rules_python_publish_deps_311_jaraco_context_sdist_9bae4ea5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2571,7 +2431,7 @@ "https://pypi.org/simple" ], "filename": "jaraco_context-6.0.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-context==6.0.1", "sha256": "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", @@ -2581,8 +2441,7 @@ } }, "rules_python_publish_deps_311_jaraco_functools_py3_none_any_ad159f13": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2596,7 +2455,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.functools-4.1.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-functools==4.1.0", "sha256": "ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", @@ -2606,8 +2465,7 @@ } }, "rules_python_publish_deps_311_jaraco_functools_sdist_70f7e0e2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2625,7 +2483,7 @@ "https://pypi.org/simple" ], "filename": "jaraco_functools-4.1.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-functools==4.1.0", "sha256": "70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", @@ -2635,8 +2493,7 @@ } }, "rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2647,7 +2504,7 @@ "cp311_linux_x86_64" ], "filename": "jeepney-0.8.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jeepney==0.8.0", "sha256": "c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", @@ -2657,8 +2514,7 @@ } }, "rules_python_publish_deps_311_jeepney_sdist_5efe48d2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2673,7 +2529,7 @@ "https://pypi.org/simple" ], "filename": "jeepney-0.8.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jeepney==0.8.0", "sha256": "5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", @@ -2683,8 +2539,7 @@ } }, "rules_python_publish_deps_311_keyring_py3_none_any_5426f817": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2698,7 +2553,7 @@ "cp311_windows_x86_64" ], "filename": "keyring-25.4.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "keyring==25.4.1", "sha256": "5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf", @@ -2708,8 +2563,7 @@ } }, "rules_python_publish_deps_311_keyring_sdist_b07ebc55": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2727,7 +2581,7 @@ "https://pypi.org/simple" ], "filename": "keyring-25.4.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "keyring==25.4.1", "sha256": "b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b", @@ -2737,8 +2591,7 @@ } }, "rules_python_publish_deps_311_markdown_it_py_py3_none_any_35521684": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2752,7 +2605,7 @@ "cp311_windows_x86_64" ], "filename": "markdown_it_py-3.0.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "markdown-it-py==3.0.0", "sha256": "355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", @@ -2762,8 +2615,7 @@ } }, "rules_python_publish_deps_311_markdown_it_py_sdist_e3f60a94": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2781,7 +2633,7 @@ "https://pypi.org/simple" ], "filename": "markdown-it-py-3.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "markdown-it-py==3.0.0", "sha256": "e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", @@ -2791,8 +2643,7 @@ } }, "rules_python_publish_deps_311_mdurl_py3_none_any_84008a41": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2806,7 +2657,7 @@ "cp311_windows_x86_64" ], "filename": "mdurl-0.1.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "mdurl==0.1.2", "sha256": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", @@ -2816,8 +2667,7 @@ } }, "rules_python_publish_deps_311_mdurl_sdist_bb413d29": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2835,7 +2685,7 @@ "https://pypi.org/simple" ], "filename": "mdurl-0.1.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "mdurl==0.1.2", "sha256": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", @@ -2845,8 +2695,7 @@ } }, "rules_python_publish_deps_311_more_itertools_py3_none_any_037b0d32": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2860,7 +2709,7 @@ "cp311_windows_x86_64" ], "filename": "more_itertools-10.5.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "more-itertools==10.5.0", "sha256": "037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", @@ -2870,8 +2719,7 @@ } }, "rules_python_publish_deps_311_more_itertools_sdist_5482bfef": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2889,7 +2737,7 @@ "https://pypi.org/simple" ], "filename": "more-itertools-10.5.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "more-itertools==10.5.0", "sha256": "5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6", @@ -2899,8 +2747,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_14c5a72e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2914,7 +2761,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", @@ -2924,8 +2771,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_7b7c2a3c": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2939,7 +2785,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", @@ -2949,8 +2795,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_aarch64_42c64511": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2964,7 +2809,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", @@ -2974,8 +2819,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_armv7l_0411beb0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2989,7 +2833,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", @@ -2999,8 +2843,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64_5f36b271": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3014,7 +2857,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", @@ -3024,8 +2867,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64le_34c03fa7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3039,7 +2881,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad", @@ -3049,8 +2891,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_s390x_19aaba96": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3064,7 +2905,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", @@ -3074,8 +2915,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_x86_64_de3ceed6": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3089,7 +2929,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", @@ -3099,8 +2939,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_aarch64_f0eca9ca": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3114,7 +2953,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe", @@ -3124,8 +2963,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_armv7l_3a157ab1": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3139,7 +2977,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", @@ -3149,8 +2987,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_x86_64_36c95d4b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3164,7 +3001,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", @@ -3174,8 +3011,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_win_amd64_8ce0f819": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3189,7 +3025,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-win_amd64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", @@ -3199,8 +3035,7 @@ } }, "rules_python_publish_deps_311_nh3_sdist_94a16692": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3218,7 +3053,7 @@ "https://pypi.org/simple" ], "filename": "nh3-0.2.18.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", @@ -3228,8 +3063,7 @@ } }, "rules_python_publish_deps_311_pkginfo_py3_none_any_889a6da2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3243,7 +3077,7 @@ "cp311_windows_x86_64" ], "filename": "pkginfo-1.10.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pkginfo==1.10.0", "sha256": "889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097", @@ -3253,8 +3087,7 @@ } }, "rules_python_publish_deps_311_pkginfo_sdist_5df73835": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3272,7 +3105,7 @@ "https://pypi.org/simple" ], "filename": "pkginfo-1.10.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pkginfo==1.10.0", "sha256": "5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", @@ -3282,8 +3115,7 @@ } }, "rules_python_publish_deps_311_pycparser_py3_none_any_c3702b6d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3294,7 +3126,7 @@ "cp311_linux_x86_64" ], "filename": "pycparser-2.22-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pycparser==2.22", "sha256": "c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", @@ -3304,8 +3136,7 @@ } }, "rules_python_publish_deps_311_pycparser_sdist_491c8be9": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3320,7 +3151,7 @@ "https://pypi.org/simple" ], "filename": "pycparser-2.22.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pycparser==2.22", "sha256": "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", @@ -3330,8 +3161,7 @@ } }, "rules_python_publish_deps_311_pygments_py3_none_any_b8e6aca0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3345,7 +3175,7 @@ "cp311_windows_x86_64" ], "filename": "pygments-2.18.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pygments==2.18.0", "sha256": "b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", @@ -3355,8 +3185,7 @@ } }, "rules_python_publish_deps_311_pygments_sdist_786ff802": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3374,7 +3203,7 @@ "https://pypi.org/simple" ], "filename": "pygments-2.18.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pygments==2.18.0", "sha256": "786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", @@ -3384,15 +3213,14 @@ } }, "rules_python_publish_deps_311_pywin32_ctypes_py3_none_any_8a151337": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ "cp311_windows_x86_64" ], "filename": "pywin32_ctypes-0.2.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pywin32-ctypes==0.2.3", "sha256": "8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", @@ -3402,8 +3230,7 @@ } }, "rules_python_publish_deps_311_pywin32_ctypes_sdist_d162dc04": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3414,7 +3241,7 @@ "https://pypi.org/simple" ], "filename": "pywin32-ctypes-0.2.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pywin32-ctypes==0.2.3", "sha256": "d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", @@ -3424,8 +3251,7 @@ } }, "rules_python_publish_deps_311_readme_renderer_py3_none_any_2fbca89b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3439,7 +3265,7 @@ "cp311_windows_x86_64" ], "filename": "readme_renderer-44.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "readme-renderer==44.0", "sha256": "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", @@ -3449,8 +3275,7 @@ } }, "rules_python_publish_deps_311_readme_renderer_sdist_8712034e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3468,7 +3293,7 @@ "https://pypi.org/simple" ], "filename": "readme_renderer-44.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "readme-renderer==44.0", "sha256": "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", @@ -3478,8 +3303,7 @@ } }, "rules_python_publish_deps_311_requests_py3_none_any_70761cfe": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3493,7 +3317,7 @@ "cp311_windows_x86_64" ], "filename": "requests-2.32.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests==2.32.3", "sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", @@ -3503,8 +3327,7 @@ } }, "rules_python_publish_deps_311_requests_sdist_55365417": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3522,7 +3345,7 @@ "https://pypi.org/simple" ], "filename": "requests-2.32.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests==2.32.3", "sha256": "55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", @@ -3532,8 +3355,7 @@ } }, "rules_python_publish_deps_311_requests_toolbelt_py2_none_any_cccfdd66": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3547,7 +3369,7 @@ "cp311_windows_x86_64" ], "filename": "requests_toolbelt-1.0.0-py2.py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests-toolbelt==1.0.0", "sha256": "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", @@ -3557,8 +3379,7 @@ } }, "rules_python_publish_deps_311_requests_toolbelt_sdist_7681a0a3": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3576,7 +3397,7 @@ "https://pypi.org/simple" ], "filename": "requests-toolbelt-1.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests-toolbelt==1.0.0", "sha256": "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", @@ -3586,8 +3407,7 @@ } }, "rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3601,7 +3421,7 @@ "cp311_windows_x86_64" ], "filename": "rfc3986-2.0.0-py2.py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rfc3986==2.0.0", "sha256": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", @@ -3611,8 +3431,7 @@ } }, "rules_python_publish_deps_311_rfc3986_sdist_97aacf9d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3630,7 +3449,7 @@ "https://pypi.org/simple" ], "filename": "rfc3986-2.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rfc3986==2.0.0", "sha256": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", @@ -3640,8 +3459,7 @@ } }, "rules_python_publish_deps_311_rich_py3_none_any_9836f509": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3655,7 +3473,7 @@ "cp311_windows_x86_64" ], "filename": "rich-13.9.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rich==13.9.3", "sha256": "9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283", @@ -3665,8 +3483,7 @@ } }, "rules_python_publish_deps_311_rich_sdist_bc1e01b8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3684,7 +3501,7 @@ "https://pypi.org/simple" ], "filename": "rich-13.9.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rich==13.9.3", "sha256": "bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e", @@ -3694,8 +3511,7 @@ } }, "rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3706,7 +3522,7 @@ "cp311_linux_x86_64" ], "filename": "SecretStorage-3.3.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "secretstorage==3.3.3", "sha256": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", @@ -3716,8 +3532,7 @@ } }, "rules_python_publish_deps_311_secretstorage_sdist_2403533e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3732,7 +3547,7 @@ "https://pypi.org/simple" ], "filename": "SecretStorage-3.3.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "secretstorage==3.3.3", "sha256": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", @@ -3742,8 +3557,7 @@ } }, "rules_python_publish_deps_311_twine_py3_none_any_215dbe7b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3757,7 +3571,7 @@ "cp311_windows_x86_64" ], "filename": "twine-5.1.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "twine==5.1.1", "sha256": "215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997", @@ -3767,8 +3581,7 @@ } }, "rules_python_publish_deps_311_twine_sdist_9aa08251": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3786,7 +3599,7 @@ "https://pypi.org/simple" ], "filename": "twine-5.1.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "twine==5.1.1", "sha256": "9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db", @@ -3796,8 +3609,7 @@ } }, "rules_python_publish_deps_311_urllib3_py3_none_any_ca899ca0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3811,7 +3623,7 @@ "cp311_windows_x86_64" ], "filename": "urllib3-2.2.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "urllib3==2.2.3", "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", @@ -3821,8 +3633,7 @@ } }, "rules_python_publish_deps_311_urllib3_sdist_e7d814a8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3840,7 +3651,7 @@ "https://pypi.org/simple" ], "filename": "urllib3-2.2.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "urllib3==2.2.3", "sha256": "e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", @@ -3850,8 +3661,7 @@ } }, "rules_python_publish_deps_311_zipp_py3_none_any_a817ac80": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3865,7 +3675,7 @@ "cp311_windows_x86_64" ], "filename": "zipp-3.20.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "zipp==3.20.2", "sha256": "a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", @@ -3875,8 +3685,7 @@ } }, "rules_python_publish_deps_311_zipp_sdist_bc9eb26f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3894,7 +3703,7 @@ "https://pypi.org/simple" ], "filename": "zipp-3.20.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "zipp==3.20.2", "sha256": "bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", @@ -3904,8 +3713,7 @@ } }, "pip_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "pip_deps", "extra_hub_aliases": {}, @@ -3921,8 +3729,7 @@ } }, "rules_fuzzing_py_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "rules_fuzzing_py_deps", "extra_hub_aliases": {}, @@ -3938,8 +3745,7 @@ } }, "rules_python_publish_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "rules_python_publish_deps", "extra_hub_aliases": {}, @@ -4005,155 +3811,150 @@ } } }, - "moduleExtensionMetadata": { - "useAllRepos": "NO", - "reproducible": false - }, "recordedRepoMappingEntries": [ [ - "bazel_features~", + "bazel_features+", "bazel_features_globals", - "bazel_features~~version_extension~bazel_features_globals" + "bazel_features++version_extension+bazel_features_globals" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_version", - "bazel_features~~version_extension~bazel_features_version" + "bazel_features++version_extension+bazel_features_version" ], [ - "rules_python~", + "rules_python+", "bazel_features", - "bazel_features~" + "bazel_features+" ], [ - "rules_python~", + "rules_python+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "rules_python~", + "rules_python+", "bazel_tools", "bazel_tools" ], [ - "rules_python~", + "rules_python+", "pypi__build", - "rules_python~~internal_deps~pypi__build" + "rules_python++internal_deps+pypi__build" ], [ - "rules_python~", + "rules_python+", "pypi__click", - "rules_python~~internal_deps~pypi__click" + "rules_python++internal_deps+pypi__click" ], [ - "rules_python~", + "rules_python+", "pypi__colorama", - "rules_python~~internal_deps~pypi__colorama" + "rules_python++internal_deps+pypi__colorama" ], [ - "rules_python~", + "rules_python+", "pypi__importlib_metadata", - "rules_python~~internal_deps~pypi__importlib_metadata" + "rules_python++internal_deps+pypi__importlib_metadata" ], [ - "rules_python~", + "rules_python+", "pypi__installer", - "rules_python~~internal_deps~pypi__installer" + "rules_python++internal_deps+pypi__installer" ], [ - "rules_python~", + "rules_python+", "pypi__more_itertools", - "rules_python~~internal_deps~pypi__more_itertools" + "rules_python++internal_deps+pypi__more_itertools" ], [ - "rules_python~", + "rules_python+", "pypi__packaging", - "rules_python~~internal_deps~pypi__packaging" + "rules_python++internal_deps+pypi__packaging" ], [ - "rules_python~", + "rules_python+", "pypi__pep517", - "rules_python~~internal_deps~pypi__pep517" + "rules_python++internal_deps+pypi__pep517" ], [ - "rules_python~", + "rules_python+", "pypi__pip", - "rules_python~~internal_deps~pypi__pip" + "rules_python++internal_deps+pypi__pip" ], [ - "rules_python~", + "rules_python+", "pypi__pip_tools", - "rules_python~~internal_deps~pypi__pip_tools" + "rules_python++internal_deps+pypi__pip_tools" ], [ - "rules_python~", + "rules_python+", "pypi__pyproject_hooks", - "rules_python~~internal_deps~pypi__pyproject_hooks" + "rules_python++internal_deps+pypi__pyproject_hooks" ], [ - "rules_python~", + "rules_python+", "pypi__setuptools", - "rules_python~~internal_deps~pypi__setuptools" + "rules_python++internal_deps+pypi__setuptools" ], [ - "rules_python~", + "rules_python+", "pypi__tomli", - "rules_python~~internal_deps~pypi__tomli" + "rules_python++internal_deps+pypi__tomli" ], [ - "rules_python~", + "rules_python+", "pypi__wheel", - "rules_python~~internal_deps~pypi__wheel" + "rules_python++internal_deps+pypi__wheel" ], [ - "rules_python~", + "rules_python+", "pypi__zipp", - "rules_python~~internal_deps~pypi__zipp" + "rules_python++internal_deps+pypi__zipp" ], [ - "rules_python~", + "rules_python+", "pythons_hub", - "rules_python~~python~pythons_hub" + "rules_python++python+pythons_hub" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_10_host", - "rules_python~~python~python_3_10_host" + "rules_python++python+python_3_10_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_11_host", - "rules_python~~python~python_3_11_host" + "rules_python++python+python_3_11_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_12_host", - "rules_python~~python~python_3_12_host" + "rules_python++python+python_3_12_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_8_host", - "rules_python~~python~python_3_8_host" + "rules_python++python+python_3_8_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_9_host", - "rules_python~~python~python_3_9_host" + "rules_python++python+python_3_9_host" ] ] } }, - "@@rules_sass~//src/toolchain:extensions.bzl%sass": { + "@@rules_sass+//src/toolchain:extensions.bzl%sass": { "general": { "bzlTransitiveDigest": "RA58Nyrsn03Z5YmQnpmBw3mqlVck++XIrx34amsqU/E=", - "usagesDigest": "FPXQ5+6+DFGdSdCMXLwFaruzstMFlLH6N0TRxi0sSH8=", + "usagesDigest": "R0KshhzIouLWuexMUCrl4HY+FUDwlVVgF9Z7UnwyUWA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "linux_amd64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_linux_x64", "sha256": "", @@ -4164,8 +3965,7 @@ } }, "linux_arm64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_linux_arm", "sha256": "", @@ -4176,8 +3976,7 @@ } }, "darwin_amd64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_mac_x64", "sha256": "", @@ -4188,8 +3987,7 @@ } }, "darwin_arm64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_mac_arm", "sha256": "", @@ -4203,81 +4001,72 @@ "recordedRepoMappingEntries": [] } }, - "@@yq.bzl~//yq:extensions.bzl%yq": { + "@@yq.bzl+//yq:extensions.bzl%yq": { "general": { "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", - "usagesDigest": "qsmXGc8TqVijif8Wnf6IWTY7tI2SyfJ4PRuGQjY4rUc=", + "usagesDigest": "RMl/keg0xfMeM4V6asCKGMUDCuo+wyQ3styJIK7uDok=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "yq_darwin_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "darwin_amd64", "version": "4.45.1" } }, "yq_darwin_arm64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "darwin_arm64", "version": "4.45.1" } }, "yq_linux_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_amd64", "version": "4.45.1" } }, "yq_linux_arm64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_arm64", "version": "4.45.1" } }, "yq_linux_s390x": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_s390x", "version": "4.45.1" } }, "yq_linux_riscv64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_riscv64", "version": "4.45.1" } }, "yq_linux_ppc64le": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_ppc64le", "version": "4.45.1" } }, "yq_windows_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "windows_amd64", "version": "4.45.1" } }, "yq_toolchains": { - "bzlFile": "@@yq.bzl~//yq/toolchain:toolchain.bzl", - "ruleClassName": "yq_toolchains_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:toolchain.bzl%yq_toolchains_repo", "attributes": { "user_repository_name": "yq" } @@ -4286,5 +4075,6 @@ "recordedRepoMappingEntries": [] } } - } + }, + "facts": {} } diff --git a/README.md b/README.md index aa6fc99b4913..baf7d7a7d94c 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Join the conversation and help the community. [angularelements]: https://angular.dev/guide/elements [ssr]: https://angular.dev/guide/ssr [schematics]: https://angular.dev/tools/cli/schematics -[lazyloading]: https://angular.dev/guide/ngmodules/lazy-loading +[lazyloading]: https://angular.dev/guide/routing/define-routes#lazily-loaded-components [node.js]: https://nodejs.org/ [npm]: https://www.npmjs.com/get-npm [codeofconduct]: CODE_OF_CONDUCT.md diff --git a/REPO.bazel b/REPO.bazel new file mode 100644 index 000000000000..9eb8128879bd --- /dev/null +++ b/REPO.bazel @@ -0,0 +1,5 @@ +ignore_directories([ + ".git", + "dist", + "**/node_modules/**", +]) diff --git a/adev/BUILD.bazel b/adev/BUILD.bazel index 0316eac26e12..478224dd036d 100644 --- a/adev/BUILD.bazel +++ b/adev/BUILD.bazel @@ -104,7 +104,6 @@ ng_application( ], env = { "NG_BUILD_PARTIAL_SSR": "1", - "NG_FORCE_TTY": "0", }, ng_config = ":ng_config", node_modules = ":node_modules", @@ -131,7 +130,6 @@ ng_application( ], env = { "NG_BUILD_OPTIMIZE_CHUNKS": "1", - "NG_FORCE_TTY": "0", }, ng_config = ":ng_config", node_modules = ":node_modules", diff --git a/adev/package.json b/adev/package.json index 2a43bb0da607..62a6a76e54f2 100644 --- a/adev/package.json +++ b/adev/package.json @@ -1,25 +1,25 @@ { "dependencies": { - "@algolia/client-common": "5.45.0", - "@algolia/client-search": "5.45.0", - "@algolia/requester-browser-xhr": "5.45.0", - "@algolia/requester-node-http": "5.45.0", + "@algolia/client-common": "5.46.0", + "@algolia/client-search": "5.46.0", + "@algolia/requester-browser-xhr": "5.46.0", + "@algolia/requester-node-http": "5.46.0", "@angular/animations": "workspace:*", - "@angular/aria": "21.1.0-next.0", - "@angular/build": "21.1.0-next.0", - "@angular/cdk": "21.1.0-next.0", - "@angular/cli": "21.1.0-next.0", + "@angular/aria": "21.1.0-next.3", + "@angular/build": "21.1.0-next.2", + "@angular/cdk": "21.1.0-next.3", + "@angular/cli": "21.1.0-next.2", "@angular/common": "workspace:*", "@angular/compiler": "workspace:*", "@angular/compiler-cli": "workspace:*", "@angular/core": "workspace:*", "@angular/docs": "workspace:*", "@angular/forms": "workspace:*", - "@angular/material": "21.1.0-next.0", + "@angular/material": "21.1.0-next.3", "@angular/platform-browser": "workspace:*", "@angular/platform-server": "workspace:*", "@angular/router": "workspace:*", - "@angular/ssr": "21.1.0-next.0", + "@angular/ssr": "21.1.0-next.2", "@codemirror/autocomplete": "6.20.0", "@codemirror/commands": "6.10.0", "@codemirror/lang-angular": "0.1.4", @@ -31,46 +31,46 @@ "@codemirror/lint": "6.9.2", "@codemirror/search": "6.5.11", "@codemirror/state": "6.5.2", - "@codemirror/view": "6.38.8", - "@lezer/common": "1.3.0", + "@codemirror/view": "6.39.4", + "@lezer/common": "1.4.0", "@lezer/css": "1.3.0", "@lezer/highlight": "1.2.3", "@lezer/html": "1.3.12", "@lezer/javascript": "1.5.4", - "@lezer/lr": "1.4.3", + "@lezer/lr": "1.4.5", "@lezer/sass": "1.1.0", "@marijn/find-cluster-break": "1.0.2", "@stackblitz/sdk": "1.11.0", "@types/dom-navigation": "1.0.6", "@types/jasmine": "5.1.13", "@types/jsdom": "27.0.0", - "@types/node": "24.10.1", + "@types/node": "24.10.4", "@typescript/vfs": "1.6.2", "@webcontainer/api": "1.6.1", "@xterm/addon-fit": "0.10.0", "@xterm/xterm": "5.5.0", - "algoliasearch": "5.45.0", + "algoliasearch": "5.46.0", "angular-split": "20.0.0", "crelt": "1.0.6", "diff": "8.0.2", "emoji-regex": "10.6.0", "fflate": "0.8.2", - "jasmine-core": "5.12.1", - "jsdom": "27.2.0", + "jasmine-core": "5.13.0", + "jsdom": "27.3.0", "karma-chrome-launcher": "3.2.0", "karma-coverage": "2.2.1", "karma-jasmine": "5.1.0", "karma-jasmine-html-reporter": "2.1.0", "marked": "17.0.1", - "mermaid": "11.12.1", + "mermaid": "11.12.2", "ngx-progressbar": "14.0.0", "open-in-idx": "0.1.1", - "playwright-core": "1.56.1", - "preact": "10.27.2", - "preact-render-to-string": "6.6.3", - "prettier": "3.6.2", + "playwright-core": "1.57.0", + "preact": "10.28.0", + "preact-render-to-string": "6.6.4", + "prettier": "3.7.4", "rxjs": "7.8.2", - "shiki": "3.15.0", + "shiki": "3.20.0", "style-mod": "4.1.3", "tinyglobby": "0.2.15", "tslib": "2.8.1", @@ -78,9 +78,9 @@ "w3c-keyname": "2.2.8" }, "devDependencies": { - "autoprefixer": "10.4.22", + "autoprefixer": "10.4.23", "karma": "~6.4.4", "postcss": "8.5.6", - "tailwindcss": "3.4.18" + "tailwindcss": "3.4.19" } } diff --git a/adev/scripts/update-cross-repo-docs/README.md b/adev/scripts/update-cross-repo-docs/README.md index 32f6e001e4e8..9dd62e307856 100644 --- a/adev/scripts/update-cross-repo-docs/README.md +++ b/adev/scripts/update-cross-repo-docs/README.md @@ -12,4 +12,4 @@ Updates the Angular CDK API JSON files in `adev/src/content/cdk`, used for the [ ## CLI Help Pages -Updates the Angular CLI help JSON files in `adev/src/content/cli/help`, used for the [angular.dev CLI](https://angular.dev/cli) pages. +Updates the Angular CLI help JSON files in `adev/src/content/cli`, used for the [angular.dev CLI](https://angular.dev/cli) pages. diff --git a/adev/scripts/update-cross-repo-docs/index.mjs b/adev/scripts/update-cross-repo-docs/index.mjs index fa56795fdabd..74e1a0e9b7ec 100644 --- a/adev/scripts/update-cross-repo-docs/index.mjs +++ b/adev/scripts/update-cross-repo-docs/index.mjs @@ -26,7 +26,7 @@ async function main() { await updateAssets({ repo: 'angular/cli-builds', assetsPath: 'help', - destPath: join(import.meta.dirname, '../../src/content/cli/help'), + destPath: join(import.meta.dirname, '../../src/content/cli'), }); console.log('\n-----------------------------------------------'); diff --git a/adev/shared-docs/components/algolia-icon/algolia-icon.component.html b/adev/shared-docs/components/algolia-icon/algolia-icon.component.html index 8f5ca876db8b..542c29b9f12d 100644 --- a/adev/shared-docs/components/algolia-icon/algolia-icon.component.html +++ b/adev/shared-docs/components/algolia-icon/algolia-icon.component.html @@ -1,52 +1,52 @@ diff --git a/adev/shared-docs/components/copy-link-anchor/BUILD.bazel b/adev/shared-docs/components/copy-link-anchor/BUILD.bazel new file mode 100644 index 000000000000..26301f43551b --- /dev/null +++ b/adev/shared-docs/components/copy-link-anchor/BUILD.bazel @@ -0,0 +1,19 @@ +load("//adev/shared-docs:defaults.bzl", "ng_project") + +ng_project( + name = "copy-link-anchor", + srcs = [ + "copy-link-anchor.component.ts", + ], + visibility = [ + "//adev/shared-docs/components:__pkg__", + "//adev/shared-docs/components/viewers:__pkg__", + ], + deps = [ + "//adev:node_modules/@angular/cdk", + "//adev:node_modules/@angular/common", + "//adev:node_modules/@angular/core", + "//adev:node_modules/@angular/material", + "//adev/shared-docs/components/icon", + ], +) diff --git a/adev/shared-docs/components/copy-link-anchor/copy-link-anchor.component.ts b/adev/shared-docs/components/copy-link-anchor/copy-link-anchor.component.ts new file mode 100644 index 000000000000..d7ff132a3e3c --- /dev/null +++ b/adev/shared-docs/components/copy-link-anchor/copy-link-anchor.component.ts @@ -0,0 +1,66 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {ChangeDetectionStrategy, Component, inject, input, signal} from '@angular/core'; +import {Clipboard} from '@angular/cdk/clipboard'; +import {MatTooltip} from '@angular/material/tooltip'; +import {IconComponent} from '../icon/icon.component'; + +export const CONFIRMATION_DISPLAY_TIME_MS = 1000; + +@Component({ + selector: 'docs-copy-link-button', + template: `{{ showCopySuccess() ? 'check' : 'link' }}`, + styles: ` + :host { + cursor: pointer; + padding: 0; + margin-left: 8px; + display: inline-flex; + align-items: center; + opacity: 0; + transition: opacity 0.3s ease; + color: var(--quaternary-contrast); + vertical-align: middle; + } + :host(.docs-copy-link-success) { + color: var(--bright-blue); + } + `, + hostDirectives: [ + { + directive: MatTooltip, + inputs: ['matTooltip'], + }, + ], + host: { + '[ariaLabel]': '"Copy link to " + label()', + '(click)': 'copyLink()', + 'matTooltipPosition': 'above', + '[class.docs-copy-link-success]': 'showCopySuccess()', + }, + imports: [IconComponent], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CopyLinkButton { + readonly href = input.required(); + readonly label = input.required(); + + protected readonly showCopySuccess = signal(false); + + private readonly clipboard = inject(Clipboard); + + copyLink(): void { + const link = `${window.location.origin}${this.href()}`; + this.clipboard.copy(link); + this.showCopySuccess.set(true); + setTimeout(() => { + this.showCopySuccess.set(false); + }, CONFIRMATION_DISPLAY_TIME_MS); + } +} diff --git a/adev/shared-docs/components/icon/BUILD.bazel b/adev/shared-docs/components/icon/BUILD.bazel index 1595f9efb34b..1fd0ff5a44cf 100644 --- a/adev/shared-docs/components/icon/BUILD.bazel +++ b/adev/shared-docs/components/icon/BUILD.bazel @@ -12,6 +12,7 @@ ng_project( ], visibility = [ "//adev/shared-docs/components:__pkg__", + "//adev/shared-docs/components/copy-link-anchor:__pkg__", "//adev/shared-docs/components/copy-source-code-button:__pkg__", "//adev/shared-docs/components/navigation-list:__pkg__", "//adev/shared-docs/components/table-of-contents:__pkg__", diff --git a/adev/shared-docs/components/navigation-list/navigation-list.component.html b/adev/shared-docs/components/navigation-list/navigation-list.component.html index e05da8af7acd..d5a65123c833 100644 --- a/adev/shared-docs/components/navigation-list/navigation-list.component.html +++ b/adev/shared-docs/components/navigation-list/navigation-list.component.html @@ -7,7 +7,7 @@ @let groupLabel = itemGroup[0]; @let items = itemGroup[1]; @if (groupLabel) { -
  • {{groupLabel | titlecase}}
  • +
  • {{ groupLabel | titlecase }}
  • } @for (item of items; track $index) {
  • @@ -39,7 +39,12 @@ - {{item.label}} + {{ item.label }} @@ -69,7 +74,7 @@ [matTooltipClass]="'API-tooltip'" > - {{item.label}} + {{ item.label }} @@ -77,7 +82,8 @@ @if ( - (item.children && item.level === expandableLevel()) || item.level === collapsableLevel() + (item.children && item.level === expandableLevel()) || + item.level === collapsableLevel() ) {
  • diff --git a/adev/shared-docs/components/search-dialog/search-dialog.component.html b/adev/shared-docs/components/search-dialog/search-dialog.component.html index f954760be7a2..a29ea4ed461f 100644 --- a/adev/shared-docs/components/search-dialog/search-dialog.component.html +++ b/adev/shared-docs/components/search-dialog/search-dialog.component.html @@ -23,7 +23,7 @@ diff --git a/adev/shared-docs/components/tab-group/tab-group.component.html b/adev/shared-docs/components/tab-group/tab-group.component.html index ff1d933db953..2969e7b64c02 100644 --- a/adev/shared-docs/components/tab-group/tab-group.component.html +++ b/adev/shared-docs/components/tab-group/tab-group.component.html @@ -9,7 +9,7 @@ [aria-controls]="tab.tabPanelId" (click)="selectedTab.set(tab.tabId)" > - {{tab.label}} + {{ tab.label }} } diff --git a/adev/shared-docs/components/table-of-contents/table-of-contents.component.html b/adev/shared-docs/components/table-of-contents/table-of-contents.component.html index 08540b5e4a4e..439e8e37ec76 100644 --- a/adev/shared-docs/components/table-of-contents/table-of-contents.component.html +++ b/adev/shared-docs/components/table-of-contents/table-of-contents.component.html @@ -6,13 +6,20 @@

    On this page

    @@ -20,4 +27,4 @@

    On this page

    arrow_upward_alt Back to the top - \ No newline at end of file + diff --git a/adev/shared-docs/components/table-of-contents/table-of-contents.component.scss b/adev/shared-docs/components/table-of-contents/table-of-contents.component.scss index 3c4e94cc4a70..5b4d22b93765 100644 --- a/adev/shared-docs/components/table-of-contents/table-of-contents.component.scss +++ b/adev/shared-docs/components/table-of-contents/table-of-contents.component.scss @@ -61,6 +61,9 @@ display: block; // to prevent overflow from the li parent padding: 0.5rem 0.5rem 0.5rem 1rem; font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } &.docs-toc-item-h3 a { @@ -96,4 +99,4 @@ button { @include mq.for-large-desktop-down { display: none; } -} \ No newline at end of file +} diff --git a/adev/shared-docs/components/top-level-banner/top-level-banner.component.html b/adev/shared-docs/components/top-level-banner/top-level-banner.component.html index 52b631153504..3c6c0800c232 100644 --- a/adev/shared-docs/components/top-level-banner/top-level-banner.component.html +++ b/adev/shared-docs/components/top-level-banner/top-level-banner.component.html @@ -4,16 +4,14 @@

    {{ text() }}

    - {{ text() - }} + {{ text() }}

    } @else {

    {{ text() }}

    - {{ text() - }} + {{ text() }}

    } diff --git a/adev/shared-docs/components/viewers/BUILD.bazel b/adev/shared-docs/components/viewers/BUILD.bazel index a5f8552943f9..4bebf424338c 100644 --- a/adev/shared-docs/components/viewers/BUILD.bazel +++ b/adev/shared-docs/components/viewers/BUILD.bazel @@ -25,6 +25,7 @@ ng_project( "//adev:node_modules/@angular/router", "//adev:node_modules/rxjs", "//adev/shared-docs/components/breadcrumb", + "//adev/shared-docs/components/copy-link-anchor", "//adev/shared-docs/components/copy-source-code-button", "//adev/shared-docs/components/icon", "//adev/shared-docs/components/tab-group", @@ -63,6 +64,7 @@ ts_project( "//adev:node_modules/@angular/platform-browser", "//adev:node_modules/@angular/router", "//adev/shared-docs/components/breadcrumb", + "//adev/shared-docs/components/copy-link-anchor", "//adev/shared-docs/components/copy-source-code-button", "//adev/shared-docs/components/icon", "//adev/shared-docs/components/table-of-contents", diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.scss b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.scss index bb53477e6b4d..9f68dda99122 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.scss +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.scss @@ -1,5 +1,4 @@ @use '../../../styles/links' as links; -@use '../../../styles/anchor' as anchor; @use '../../../styles/media-queries' as mq; :host { @@ -56,8 +55,6 @@ color: inherit; max-width: 100%; - @include anchor.docs-anchor(); - code { // This can be useful on mobile if the heading contains a long code block overflow: hidden; @@ -65,6 +62,11 @@ vertical-align: middle; } } + + // Show copy link button when hovering the heading + &:hover docs-copy-link-button { + opacity: 1; + } } h1 { diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts index b6be1fe5cd21..1e901f404b26 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts @@ -17,8 +17,10 @@ import {IconComponent} from '../../icon/icon.component'; import {Breadcrumb} from '../../breadcrumb/breadcrumb.component'; import {NavigationState} from '../../../services'; import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component'; +import {CopyLinkButton} from '../../copy-link-anchor/copy-link-anchor.component'; import {TableOfContents} from '../../table-of-contents/table-of-contents.component'; import {provideZonelessChangeDetection} from '@angular/core'; +import {Clipboard} from '@angular/cdk/clipboard'; describe('DocViewer', () => { let fixture: ComponentFixture; @@ -70,6 +72,12 @@ describe('DocViewer', () => {

    Heading h3

    `; + const exampleContentWithDocsAnchor = ` +

    + Test Section +

    + `; + beforeEach(() => { exampleContentSpy = jasmine.createSpyObj('ExampleViewerContentLoader', ['getCodeExampleData']); navigationStateSpy = jasmine.createSpyObj(NavigationState, ['activeNavigationItem']); @@ -87,13 +95,12 @@ describe('DocViewer', () => { }); fixture = TestBed.createComponent(DocViewer); - fixture.detectChanges(); }); - it('should load doc into innerHTML', () => { + it('should load doc into innerHTML', async () => { const fixture = TestBed.createComponent(DocViewer); fixture.componentRef.setInput('docContent', 'hello world'); - fixture.detectChanges(); + await fixture.whenStable(); expect(fixture.nativeElement.innerHTML).toBe('hello world'); }); @@ -101,7 +108,6 @@ describe('DocViewer', () => { it('should instantiate example viewer with only a single file', async () => { const fixture = TestBed.createComponent(DocViewer); fixture.componentRef.setInput('docContent', exampleDocContentWithExampleViewerPlaceholders); - fixture.detectChanges(); await fixture.whenStable(); const exampleViewer = fixture.debugElement.query(By.directive(ExampleViewer)); @@ -125,7 +131,6 @@ describe('DocViewer', () => { 'docContent', exampleDocContentWithExpandedExampleViewerPlaceholders, ); - fixture.detectChanges(); await fixture.whenStable(); const exampleViewer = fixture.debugElement.query(By.directive(ExampleViewer)); @@ -139,7 +144,6 @@ describe('DocViewer', () => { const renderComponentSpy = spyOn(fixture.componentInstance, 'renderComponent' as any); fixture.componentRef.setInput('docContent', exampleContentWithIcons); - fixture.detectChanges(); await fixture.whenStable(); expect(renderComponentSpy).toHaveBeenCalledTimes(2); @@ -165,7 +169,6 @@ describe('DocViewer', () => { const renderComponentSpy = spyOn(fixture.componentInstance, 'renderComponent' as any); fixture.componentRef.setInput('docContent', exampleContentWithBreadcrumbPlaceholder); - fixture.detectChanges(); await fixture.whenStable(); expect(renderComponentSpy).toHaveBeenCalledTimes(1); @@ -176,7 +179,6 @@ describe('DocViewer', () => { const fixture = TestBed.createComponent(DocViewer); fixture.componentRef.setInput('docContent', exampleContentWithCodeSnippet); - fixture.detectChanges(); await fixture.whenStable(); const copySourceCodeButton = fixture.debugElement.query(By.directive(CopySourceCodeButton)); @@ -190,7 +192,6 @@ describe('DocViewer', () => { fixture.componentRef.setInput('docContent', exampleContentWithHeadings); fixture.componentRef.setInput('hasToc', true); - fixture.detectChanges(); await fixture.whenStable(); expect(renderComponentSpy).toHaveBeenCalled(); @@ -203,9 +204,42 @@ describe('DocViewer', () => { fixture.componentRef.setInput('docContent', exampleContentWithHeadings); fixture.componentRef.setInput('hasToc', false); - fixture.detectChanges(); await fixture.whenStable(); expect(renderComponentSpy).not.toHaveBeenCalled(); }); + + it('should setup copy link functionality for docs-anchor elements', async () => { + const fixture = TestBed.createComponent(DocViewer); + fixture.componentRef.setInput('docContent', exampleContentWithDocsAnchor); + + await fixture.whenStable(); + + const copyLinkButton = fixture.debugElement.query(By.directive(CopyLinkButton)); + expect(copyLinkButton).toBeTruthy(); + + const anchor = fixture.nativeElement.querySelector('a.docs-anchor'); + expect(anchor).toBeTruthy(); + + const copyButton = fixture.nativeElement.querySelector('docs-copy-link-button'); + expect(copyButton).toBeTruthy(); + }); + + it('should copy link to clipboard when copy button is clicked', async () => { + const clipboard = TestBed.inject(Clipboard); + const clipboardSpy = spyOn(clipboard, 'copy').and.returnValue(true); + + const fixture = TestBed.createComponent(DocViewer); + fixture.componentRef.setInput('docContent', exampleContentWithDocsAnchor); + + await fixture.whenStable(); + + const copyButton = fixture.nativeElement.querySelector('docs-copy-link-button'); + copyButton.click(); + + expect(clipboardSpy).toHaveBeenCalled(); + // Because the copyButton click bubbles up to an anchor tag, causing a navigation, it is + // necessary to undo this location change by going back in the history. + window.history.back(); + }); }); diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts index df0b073034a1..f6ae2d647c58 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts @@ -39,6 +39,7 @@ import {TableOfContents} from '../../table-of-contents/table-of-contents.compone import {DomSanitizer} from '@angular/platform-browser'; import {Breadcrumb} from '../../breadcrumb/breadcrumb.component'; import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component'; +import {CopyLinkButton} from '../../copy-link-anchor/copy-link-anchor.component'; import {ExampleViewer} from '../example-viewer/example-viewer.component'; import {TabGroup} from '../../tab-group/tab-group.component'; @@ -120,6 +121,8 @@ export class DocViewer { // In case when content contains static code snippets, then create buttons // responsible for copy source code. this.loadCopySourceCodeButtons(); + // Setup copy link functionality for section anchor links + this.loadCopyLinkAnchors(contentContainer); // In case when content contains tabs, create tabs component and move // content in a tab into tab panel. this.constructTabs(contentContainer); @@ -202,6 +205,7 @@ export class DocViewer { const preview = Boolean(placeholder.getAttribute('preview')); const hideCode = Boolean(placeholder.getAttribute('hideCode')); const title = placeholder.getAttribute('header') ?? undefined; + const style = placeholder.getAttribute('style') ?? undefined; const firstCodeSnippetTitle = snippets.length > 0 ? (snippets[0].title ?? snippets[0].name) : undefined; const exampleRef = this.viewContainer.createComponent(ExampleViewer); @@ -214,6 +218,7 @@ export class DocViewer { preview, hideCode, id: this.countOfExamples, + style, }); exampleRef.setInput('githubUrl', `${GITHUB_CONTENT_URL}/${snippets[0].name}`); @@ -266,7 +271,9 @@ export class DocViewer { private loadCopySourceCodeButtons(): void { const staticCodeSnippets = ( Array.from( - this.elementRef.nativeElement.querySelectorAll('.docs-code:not([mermaid],.docs-no-copy)'), + this.elementRef.nativeElement.querySelectorAll( + '.docs-code:not([mermaid],[hideCopy],.docs-no-copy)', + ), ) ); @@ -276,6 +283,22 @@ export class DocViewer { } } + private loadCopyLinkAnchors(element: HTMLElement): void { + const docsAnchors = Array.from(element.querySelectorAll('a.docs-anchor')); + + for (const anchor of docsAnchors) { + const href = anchor.getAttribute('href')!; + const label = anchor.textContent!; + + const copyLinkButtonRef = this.viewContainer.createComponent(CopyLinkButton); + copyLinkButtonRef.setInput('href', href); + copyLinkButtonRef.setInput('label', label); + copyLinkButtonRef.setInput('matTooltip', `Copy link to ${label}`); + + anchor.appendChild(copyLinkButtonRef.location.nativeElement); + } + } + private loadBreadcrumb(element: HTMLElement): void { const breadcrumbPlaceholder = element.querySelector('docs-breadcrumb') as HTMLElement; const activeNavigationItem = this.navigationState.activeNavigationItem(); diff --git a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts index d3c6415166aa..023314edf981 100644 --- a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts +++ b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts @@ -233,8 +233,7 @@ describe('ExampleViewer', () => { expect(component.expanded()).toBeFalse(); }); - // TODO(josephperrott): enable once the docs-viewer/example-viewer circle is sorted out. - xit('should call clipboard service when clicked on copy source code', async () => { + it('should call clipboard service when clicked on copy source code', async () => { const expectedCodeSnippetContent = 'typescript code'; componentRef.setInput( 'metadata', @@ -252,6 +251,7 @@ describe('ExampleViewer', () => { const spy = spyOn(clipboardService, 'copy'); await component.renderExample(); + await fixture.whenStable(); const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; button.click(); @@ -270,7 +270,8 @@ describe('ExampleViewer', () => { By.css('button.docs-example-copy-link'), ).nativeElement; button.click(); - expect(spy.calls.argsFor(0)[0].trim()).toBe(`${window.location.href}#example-1`); + const expectedUrl = location.origin + location.pathname + location.search + '#example-1'; + expect(spy.calls.argsFor(0)[0].trim()).toBe(expectedUrl); }); it('should hide code content when `hideCode` is true', async () => { @@ -346,6 +347,7 @@ const getMetadata = (value: Partial = {}): ExampleMetadata => { preview: false, hideCode: false, ...value, + style: undefined, }; }; diff --git a/adev/shared-docs/index.ts b/adev/shared-docs/index.ts index 9a87abfc7c70..bb968e92d555 100644 --- a/adev/shared-docs/index.ts +++ b/adev/shared-docs/index.ts @@ -13,6 +13,7 @@ export * from './directives/index'; export * from './components/index'; export * from './interfaces/index'; +export * from './pipes/index'; export * from './providers/index'; export * from './services/index'; export * from './utils/index'; diff --git a/adev/shared-docs/interfaces/code-example.ts b/adev/shared-docs/interfaces/code-example.ts index 46fe274109ce..d43580f8c209 100644 --- a/adev/shared-docs/interfaces/code-example.ts +++ b/adev/shared-docs/interfaces/code-example.ts @@ -42,4 +42,6 @@ export interface ExampleMetadata { preview: boolean; /** Whether to hide code example by default. */ hideCode: boolean; + /** Visual style for the code example */ + style?: 'prefer' | 'avoid'; } diff --git a/adev/shared-docs/package.json b/adev/shared-docs/package.json index dacfaea63208..49f521c4912e 100644 --- a/adev/shared-docs/package.json +++ b/adev/shared-docs/package.json @@ -18,7 +18,7 @@ "diff": "~8.0.0", "emoji-regex": "~10.6.0", "fflate": "^0.8.2", - "jsdom": "~27.2.0", + "jsdom": "~27.3.0", "marked": "~17.0.0", "mermaid": "^11.0.0", "shiki": "^3.0.0", diff --git a/adev/shared-docs/pipeline/api-gen/manifest/generate_manifest.mts b/adev/shared-docs/pipeline/api-gen/manifest/generate_manifest.mts index 9f41f144394f..c2e820dc43ee 100644 --- a/adev/shared-docs/pipeline/api-gen/manifest/generate_manifest.mts +++ b/adev/shared-docs/pipeline/api-gen/manifest/generate_manifest.mts @@ -11,6 +11,7 @@ import type {DocEntry, EntryCollection, FunctionEntry, JsDocTagEntry} from '@ang export interface ManifestEntry { name: string; + aliases?: string[]; type: string; category: string | undefined; deprecated: {version: string | undefined} | undefined; @@ -81,6 +82,7 @@ export function generateManifest(apiCollections: EntryCollection[]): Manifest { .filter((entry) => !isHiddenEntry(entry)) .map((entry: DocEntry) => ({ name: entry.name, + ...(entry.aliases && {aliases: entry.aliases}), type: entry.entryType, deprecated: getTagSinceVersion(entry, 'deprecated', true), developerPreview: getTagSinceVersion(entry, 'developerPreview'), diff --git a/adev/shared-docs/pipeline/api-gen/rendering/entities/categorization.mts b/adev/shared-docs/pipeline/api-gen/rendering/entities/categorization.mts index a934398c5332..9c82e58e18bc 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/entities/categorization.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/entities/categorization.mts @@ -174,8 +174,9 @@ function getTag(entry: T, tag: string, e export function getTagSinceVersion( entry: T, tagName: string, + every = false, ): {version: string | undefined} | undefined { - const tag = getTag(entry, tagName); + const tag = getTag(entry, tagName, every); if (!tag) { return undefined; } diff --git a/adev/shared-docs/pipeline/api-gen/rendering/index.mts b/adev/shared-docs/pipeline/api-gen/rendering/index.mts index 7316c9ff5340..76719f394813 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/index.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/index.mts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs'; +import {mkdirSync, readFileSync, writeFileSync} from 'fs'; import path from 'path'; import {CliCommand} from './cli-entities.mjs'; import {DocEntry} from './entities.mjs'; diff --git a/adev/shared-docs/pipeline/api-gen/rendering/render_api_to_html.bzl b/adev/shared-docs/pipeline/api-gen/rendering/render_api_to_html.bzl index e569ebfa3d8b..0d0e12ce1ca9 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/render_api_to_html.bzl +++ b/adev/shared-docs/pipeline/api-gen/rendering/render_api_to_html.bzl @@ -24,6 +24,7 @@ def _render_api_to_html(ctx): executable = ctx.executable._render_api_to_html, outputs = outputs, arguments = [args], + mnemonic = "RenderApiToHtml", env = { "BAZEL_BINDIR": ".", }, diff --git a/adev/shared-docs/pipeline/api-gen/rendering/styling/css-classes.mts b/adev/shared-docs/pipeline/api-gen/rendering/styling/css-classes.mts index 760b0985d654..af2fcfbe5ca6 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/styling/css-classes.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/styling/css-classes.mts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -// TODO(jelbourn): all of these CSS classes should use the `docs-` prefix. - export const API_REFERENCE_CONTAINER = 'docs-api'; export const PARAM_KEYWORD_CLASS_NAME = 'docs-param-keyword'; diff --git a/adev/shared-docs/pipeline/api-gen/rendering/symbol-context.mts b/adev/shared-docs/pipeline/api-gen/rendering/symbol-context.mts index 3477eef3067a..2882a4b17336 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/symbol-context.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/symbol-context.mts @@ -21,8 +21,15 @@ export function setCurrentSymbol(symbol: string): void { currentSymbol = symbol; } -export function getSymbols() { - return symbols; +/** Convert Record to ApiEntries format */ +export function getSymbolsAsApiEntries(): Record { + const result: Record = {}; + + for (const symbol in symbols) { + result[symbol] = {moduleName: symbols[symbol]}; + } + + return result; } export function getCurrentSymbol(): string | undefined { @@ -34,7 +41,7 @@ export function setSymbols(newSymbols: Record): void { } export function getSymbolUrl(symbol: string): string | undefined { - return sharedGetSymbolUrl(symbol, symbols); + return sharedGetSymbolUrl(symbol, getSymbolsAsApiEntries()); } export function unknownSymbolMessage(link: string, symbol: string): string { diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/class-member.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/class-member.tsx index 08406d63da53..684b34025af0 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/class-member.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/class-member.tsx @@ -17,7 +17,6 @@ import { } from '../entities/categorization.mjs'; import {MemberEntryRenderable, MethodEntryRenderable} from '../entities/renderables.mjs'; import { - PARAM_KEYWORD_CLASS_NAME, REFERENCE_MEMBER_CARD, REFERENCE_MEMBER_CARD_BODY, REFERENCE_MEMBER_CARD_HEADER, @@ -37,7 +36,7 @@ export function ClassMember(props: {member: MemberEntryRenderable}) { const signature = method.signatures.length ? method.signatures : [method.implementation]; return signature.map((sig) => { const renderableMember = getFunctionMetadataRenderable(sig, method.moduleName, method.repo); - return ; + return ; }); }; diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/class-method-info.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/class-method-info.tsx index c471b64a6a9b..b37d5d5e83e2 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/class-method-info.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/class-method-info.tsx @@ -7,7 +7,10 @@ */ import {Fragment, h} from 'preact'; -import {FunctionSignatureMetadataRenderable, ParameterEntryRenderable} from '../entities/renderables.mjs'; +import { + FunctionSignatureMetadataRenderable, + ParameterEntryRenderable, +} from '../entities/renderables.mjs'; import {PARAM_KEYWORD_CLASS_NAME, REFERENCE_MEMBER_CARD_ITEM} from '../styling/css-classes.mjs'; import {DeprecatedLabel} from './deprecated-label'; import {Parameter} from './parameter'; @@ -17,12 +20,7 @@ import {CodeSymbol} from './code-symbols'; /** * Component to render the method-specific parts of a class's API reference. */ -export function ClassMethodInfo(props: { - entry: FunctionSignatureMetadataRenderable; - options?: { - showUsageNotes?: boolean; - }; -}) { +export function ClassMethodInfo(props: {entry: FunctionSignatureMetadataRenderable}) { const entry = props.entry; return ( @@ -45,9 +43,9 @@ export function ClassMethodInfo(props: { @returns - {entry.htmlUsageNotes && props.options?.showUsageNotes ? ( + {entry.htmlUsageNotes ? (
    - Usage notes + Usage notes
    ) : ( diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line-group.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line-group.tsx index 2bf288fa1928..b5db016dc261 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line-group.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line-group.tsx @@ -12,13 +12,13 @@ import {CodeLine} from './code-line'; export function CodeLineGroup(props: {lines: CodeLineRenderable[]}) { if (props.lines.length > 1) { - return (
    - { - props.lines.map(line => - - ) - } -
    ); + return ( +
    + {props.lines.map((line) => ( + + ))} +
    + ); } else { return ; } diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line.tsx index 64b2ae498f8b..6d0b3e0c5390 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/code-line.tsx @@ -18,7 +18,7 @@ export function CodeLine(props: {line: CodeLineRenderable}) { const pattern = /]*\bclass=["']line["'][^>]*>(.*)<\/span>/s; const match = line.contents.match(pattern); - // + // let highlightedContent = match ? match[1] : line.contents; if (line.id) { diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/header-api.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/header-api.tsx index f9d0cd3aedb0..cfdcc84b8313 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/header-api.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/header-api.tsx @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Fragment, h} from 'preact'; +import {Fragment, h, HTMLAttributes} from 'preact'; import {EntryType, isDocEntryWithSourceInfo, PipeEntry} from '../entities.mjs'; import {DocEntryRenderable, PipeEntryRenderable} from '../entities/renderables.mjs'; import { @@ -73,7 +73,7 @@ export function HeaderApi(props: { } function statusTag(entry: DocEntryRenderable) { - let tag: h.JSX.HTMLAttributes | null = null; + let tag: HTMLAttributes | null = null; // Cascading Deprecated > Stable > Developer Preview > Experimental @@ -123,13 +123,6 @@ function tagInVersionString(label: string, tag: {version: string | undefined} | return <>{label}; } -function tagInVersionTooltip( - label: string, - tag: {version: string | undefined} | undefined, -): string { - return tag?.version ? `${label} since ${tag.version}` : label; -} - function getEntryTypeDisplayName(entryType: EntryType | string): string { switch (entryType) { case EntryType.NgModule: diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/header-cli.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/header-cli.tsx index 1493f55d2f4a..54f5f8d83bda 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/header-cli.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/header-cli.tsx @@ -8,7 +8,12 @@ import {h} from 'preact'; import {CliCommandRenderable} from '../entities/renderables.mjs'; -import {HEADER_CLASS_NAME, HEADER_ENTRY_CATEGORY, HEADER_ENTRY_LABEL, HEADER_ENTRY_TITLE} from '../styling/css-classes.mjs'; +import { + HEADER_CLASS_NAME, + HEADER_ENTRY_CATEGORY, + HEADER_ENTRY_LABEL, + HEADER_ENTRY_TITLE, +} from '../styling/css-classes.mjs'; /** Component to render a header of the CLI page. */ export function HeaderCli(props: {command: CliCommandRenderable}) { @@ -20,7 +25,9 @@ export function HeaderCli(props: {command: CliCommandRenderable}) {
    -

    {command.parentCommand?.name} {command.name}

    +

    + {command.parentCommand?.name} {command.name} +

    {'Command'}
    diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/parameter.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/parameter.tsx index 55e86703ca16..0c8f5356ffa3 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/parameter.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/parameter.tsx @@ -17,12 +17,12 @@ export function Parameter(props: {param: ParameterEntryRenderable}) { const param = props.param; return ( -
    - {/*TODO: isOptional, isRestParam*/} - @param - {param.name} - - -
    +
    + {/*TODO: isOptional, isRestParam*/} + @param + {param.name} + + +
    ); } diff --git a/adev/shared-docs/pipeline/api-gen/rendering/templates/section-heading.tsx b/adev/shared-docs/pipeline/api-gen/rendering/templates/section-heading.tsx index 2963acb1833f..7544b3cb6763 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/templates/section-heading.tsx +++ b/adev/shared-docs/pipeline/api-gen/rendering/templates/section-heading.tsx @@ -17,7 +17,7 @@ export function SectionHeading(props: {name: string}) { return (

    - + {props.name}

    diff --git a/adev/shared-docs/pipeline/api-gen/rendering/test/transforms/jsdoc-transforms.spec.mts b/adev/shared-docs/pipeline/api-gen/rendering/test/transforms/jsdoc-transforms.spec.mts index 947d7697eb81..bcf7f0227033 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/test/transforms/jsdoc-transforms.spec.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/test/transforms/jsdoc-transforms.spec.mts @@ -9,7 +9,11 @@ import {initHighlighter} from '../../../../shared/shiki.mjs'; import {setHighlighterInstance} from '../../shiki/shiki.mjs'; import {setCurrentSymbol, setSymbols} from '../../symbol-context.mjs'; -import {addHtmlAdditionalLinks, addHtmlDescription} from '../../transforms/jsdoc-transforms.mjs'; +import { + addHtmlAdditionalLinks, + addHtmlDescription, + setEntryFlags, +} from '../../transforms/jsdoc-transforms.mjs'; // @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context. describe('jsdoc transforms', () => { @@ -230,4 +234,62 @@ function setupRouter() { expect(entry.htmlDescription).toContain('/api/angular/router/Router'); }); + + it('should only mark as deprecated if all overloads are deprecated', () => { + const entry = setEntryFlags({ + name: 'Injectable', + jsdocTags: [ + {'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'}, + { + 'name': 'see', + 'comment': '[Creating and using services](guide/di/creating-and-using-services)', + }, + ], + signatures: [ + { + 'parameters': [], + 'jsdocTags': [{'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'}], + }, + { + 'parameters': [], + 'jsdocTags': [ + { + 'name': 'deprecated', + 'comment': + "The `providedIn: NgModule` or `providedIn:'any'` options are deprecated. Please use the other signatures.", + }, + ], + }, + ], + } as any); + + expect(entry.deprecated).toEqual(undefined); + + const deprecatedEntry = setEntryFlags({ + name: 'Injectable', + jsdocTags: [ + {'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'}, + { + 'name': 'see', + 'comment': '[Creating and using services](guide/di/creating-and-using-services)', + }, + ], + signatures: [ + { + 'parameters': [], + 'jsdocTags': [ + {'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'}, + {'name': 'deprecated', 'comment': '19.0 something something'}, + ], + }, + { + 'parameters': [], + 'jsdocTags': [{'name': 'deprecated', 'comment': '19.0 something something'}], + }, + ], + } as any); + + // It's deprecated by + expect(deprecatedEntry.deprecated).toEqual({version: '19.0'}); + }); }); diff --git a/adev/shared-docs/pipeline/api-gen/rendering/transforms/jsdoc-transforms.mts b/adev/shared-docs/pipeline/api-gen/rendering/transforms/jsdoc-transforms.mts index 350d36b47091..87dd75ad11b6 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/transforms/jsdoc-transforms.mts +++ b/adev/shared-docs/pipeline/api-gen/rendering/transforms/jsdoc-transforms.mts @@ -28,7 +28,7 @@ import {parseMarkdown} from '../../../shared/marked/parse.mjs'; import {getHighlighterInstance} from '../shiki/shiki.mjs'; import { getCurrentSymbol, - getSymbols, + getSymbolsAsApiEntries, getSymbolUrl, unknownSymbolMessage, } from '../symbol-context.mjs'; @@ -107,7 +107,7 @@ export function addHtmlUsageNotes(entry: T): T & HasHtml function getHtmlForJsDocText(text: string): string { const mdToParse = convertLinks(wrapExampleHtmlElementsWithCode(text)); const parsed = parseMarkdown(mdToParse, { - apiEntries: getSymbols(), + apiEntries: getSymbolsAsApiEntries(), highlighter: getHighlighterInstance(), }); return addApiLinksToHtml(parsed); @@ -119,7 +119,7 @@ export function setEntryFlags( const deprecationMessage = getDeprecatedEntry(entry); return { ...entry, - deprecated: getTagSinceVersion(entry, 'deprecated'), + deprecated: getTagSinceVersion(entry, 'deprecated', true), deprecationMessage: deprecationMessage ? getHtmlForJsDocText(deprecationMessage) : deprecationMessage, diff --git a/adev/shared-docs/pipeline/examples/template/src/index.html b/adev/shared-docs/pipeline/examples/template/src/index.html index cb4be54ab556..394c649380f1 100644 --- a/adev/shared-docs/pipeline/examples/template/src/index.html +++ b/adev/shared-docs/pipeline/examples/template/src/index.html @@ -1,14 +1,17 @@ - - - Example - - - - - - - - + + + Example + + + + + + + + diff --git a/adev/shared-docs/pipeline/guides/index.mts b/adev/shared-docs/pipeline/guides/index.mts index 303f4a2e9eae..056caa2b0b68 100644 --- a/adev/shared-docs/pipeline/guides/index.mts +++ b/adev/shared-docs/pipeline/guides/index.mts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {readFileSync, writeFileSync} from 'fs'; +import {readFile, writeFile} from 'fs/promises'; import path from 'path'; import {initHighlighter} from '../shared/shiki.mjs'; import {parseMarkdownAsync} from '../shared/marked/parse.mjs'; @@ -15,61 +15,65 @@ import {hasUnknownAnchors} from './helpers.mjs'; type ApiManifest = ApiManifestPackage[]; interface ApiManifestPackage { moduleName: string; - entries: {name: string}[]; + entries: {name: string; aliases?: string[]}[]; } async function main() { const [paramFilePath] = process.argv.slice(2); - const rawParamLines = readFileSync(paramFilePath, {encoding: 'utf8'}).split('\n'); + const rawParamLines = (await readFile(paramFilePath, {encoding: 'utf8'})).split('\n'); const [srcs, outputFilenameExecRootRelativePath, apiManifestPath] = rawParamLines; // The highlighter needs to be setup asynchronously // so we're doing it at the start of the pipeline const highlighter = await initHighlighter(); - for (const filePath of srcs.split(',')) { - if (!filePath.endsWith('.md')) { - throw new Error(`Input file "${filePath}" does not end in a ".md" file extension.`); - } + await Promise.all( + srcs.split(',').map(async (filePath) => { + if (!filePath.endsWith('.md')) { + throw new Error(`Input file "${filePath}" does not end in a ".md" file extension.`); + } - let apiManifest: ApiManifest = []; - if (apiManifestPath) { - try { - const apiManifestStr = readFileSync(apiManifestPath, {encoding: 'utf8'}); - apiManifest = JSON.parse(apiManifestStr); - } catch (error) { - console.warn('Failed to load API entries:', error); + let apiManifest: ApiManifest = []; + if (apiManifestPath) { + try { + const apiManifestStr = await readFile(apiManifestPath, {encoding: 'utf8'}); + apiManifest = JSON.parse(apiManifestStr); + } catch (error) { + console.warn('Failed to load API entries:', error); + } } - } - const markdownContent = readFileSync(filePath, {encoding: 'utf8'}); - const htmlOutputContent = await parseMarkdownAsync(markdownContent, { - markdownFilePath: filePath, - apiEntries: mapManifestToEntries(apiManifest), - highlighter, - }); + const markdownContent = await readFile(filePath, {encoding: 'utf8'}); + const htmlOutputContent = await parseMarkdownAsync(markdownContent, { + markdownFilePath: filePath, + apiEntries: mapManifestToEntries(apiManifest), + highlighter, + }); - // The expected file name structure is the [name of the file].md.html. - const htmlFileName = filePath + '.html'; - const htmlOutputPath = path.join(outputFilenameExecRootRelativePath, htmlFileName); + // The expected file name structure is the [name of the file].md.html. + const htmlFileName = filePath + '.html'; + const htmlOutputPath = path.join(outputFilenameExecRootRelativePath, htmlFileName); - const unknownAnchor = hasUnknownAnchors(htmlOutputContent); - if (unknownAnchor) { - throw new Error( - `The file "${filePath}" contains an anchor link to "${unknownAnchor}" which does not exist in the document.`, - ); - } + const unknownAnchor = hasUnknownAnchors(htmlOutputContent); + if (unknownAnchor) { + throw new Error( + `The file "${filePath}" contains an anchor link to "${unknownAnchor}" which does not exist in the document.`, + ); + } - writeFileSync(htmlOutputPath, htmlOutputContent, {encoding: 'utf8'}); - } + await writeFile(htmlOutputPath, htmlOutputContent, {encoding: 'utf8'}); + }), + ); } main(); -function mapManifestToEntries(apiManifest: ApiManifest): Record { +function mapManifestToEntries( + apiManifest: ApiManifest, +): Record { const duplicateEntries = new Set(); - const entryToModuleMap: Record = {}; + const entryToModuleMap: Record = {}; for (const pkg of apiManifest) { for (const entry of pkg.entries) { if (duplicateEntries.has(entry.name)) { @@ -78,7 +82,19 @@ function mapManifestToEntries(apiManifest: ApiManifest): Record delete entryToModuleMap[entry.name]; duplicateEntries.add(entry.name); } else { - entryToModuleMap[entry.name] = pkg.moduleName.replace(/^@angular\//, ''); + const normalizedModuleName = pkg.moduleName.replace(/^@angular\//, ''); + + entryToModuleMap[entry.name] = {moduleName: normalizedModuleName}; + + // If there are aliases, create entries for each alias + if (entry.aliases) { + for (const alias of entry.aliases) { + entryToModuleMap[alias] = { + moduleName: normalizedModuleName, + targetSymbol: entry.name, + }; + } + } } } } diff --git a/adev/shared-docs/pipeline/shared/BUILD.bazel b/adev/shared-docs/pipeline/shared/BUILD.bazel index 4edd36322ea4..851ca4f99e6e 100644 --- a/adev/shared-docs/pipeline/shared/BUILD.bazel +++ b/adev/shared-docs/pipeline/shared/BUILD.bazel @@ -1,4 +1,4 @@ -load("//adev/shared-docs:defaults.bzl", "ts_project") +load("//adev/shared-docs:defaults.bzl", "ts_project", "zoneless_jasmine_test") package(default_visibility = ["//visibility:public"]) @@ -20,3 +20,17 @@ ts_project( "//adev:node_modules/shiki", ], ) + +ts_project( + name = "linking_test_lib", + testonly = True, + srcs = ["test/linking.spec.mts"], + deps = [ + ":linking", + ], +) + +zoneless_jasmine_test( + name = "linking_test", + data = [":linking_test_lib"], +) diff --git a/adev/shared-docs/pipeline/shared/linking.mts b/adev/shared-docs/pipeline/shared/linking.mts index 5a1a0074d86f..bf01f6df2c79 100644 --- a/adev/shared-docs/pipeline/shared/linking.mts +++ b/adev/shared-docs/pipeline/shared/linking.mts @@ -10,13 +10,22 @@ // Example when there is a conflict between API entries and compiler features. // eg: "animate" is both an Animation API entry and an template instruction "animation.enter" // or "style" is a generic term but also an Animation API entry. -const LINK_EXEMPT = new Set(['animate', 'animate.enter', 'animate.leave', 'style']); +const LINK_EXEMPT = new Set([ + 'animate', + 'animate.enter', + 'animate.leave', + 'style', + 'readonly', + 'disabled', + 'hidden', +]); export function shouldLinkSymbol(symbol: string): boolean { return !LINK_EXEMPT.has(symbol); } -export type ApiEntries = Record; // symbolName -> moduleName (without @angular/ prefix) +// symbolName -> symbol info with moduleName and optional targetSymbol for aliases +export type ApiEntries = Record; /** * Extracts the symbol name and property name from a symbol string. @@ -53,9 +62,15 @@ export function getSymbolUrl(symbol: string, apiEntries: ApiEntries): string | u const {symbolName, propName} = extractFromSymbol(symbol); // We don't want to match entries like "constructor" - const apiEntry = Object.hasOwn(apiEntries, symbolName) && apiEntries[symbolName]; + if (!Object.hasOwn(apiEntries, symbolName)) { + return undefined; + } + + const apiEntry = apiEntries[symbolName]; + const moduleName = apiEntry.moduleName; + const targetSymbol = apiEntry.targetSymbol ?? symbolName; - return apiEntry ? `/api/${apiEntry}/${symbolName}${propName ? `#${propName}` : ''}` : undefined; + return `/api/${moduleName}/${targetSymbol}${propName ? `#${propName}` : ''}`; } function hasMoreThanOneDot(str: string) { diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts index 9963e28016a3..3d8cfc0fe013 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts @@ -8,13 +8,13 @@ import {Tokens, Token, RendererThis, TokenizerThis} from 'marked'; import {loadWorkspaceRelativeFile} from '../../helpers.mjs'; -import {formatHeading} from '../../transformations/heading.mjs'; interface DocsCardContainerToken extends Tokens.Generic { type: 'docs-card-container'; cards: string; headerTitle?: string; headerImgSrc?: string; + header: Tokens.Heading; tokens: Token[]; } @@ -40,13 +40,20 @@ export const docsCardContainerExtension = { const headerImgSrc = headerImgSrcRule.exec(attr); const body = match[2].trim(); + const header = headerTitle ? headerTitle[1] : ''; const token: DocsCardContainerToken = { type: 'docs-card-container', raw: match[0], cards: body ?? '', - headerTitle: headerTitle ? headerTitle[1] : undefined, headerImgSrc: headerImgSrc ? headerImgSrc[1] : undefined, + header: { + text: header, + raw: header, + tokens: this.lexer.inlineTokens(header, []), + type: 'heading', + depth: 2, + }, tokens: [], }; this.lexer.blockTokens(token.cards, token.tokens); @@ -55,7 +62,7 @@ export const docsCardContainerExtension = { return undefined; }, renderer(this: RendererThis, token: DocsCardContainerToken) { - return token.headerTitle + return token.header.text ? getContainerWithHeader(this, token) : getStandardContainer(this, token); }, @@ -78,7 +85,7 @@ function getContainerWithHeader(renderer: RendererThis, token: DocsCardContainer return `
    - ${formatHeading({text: token.headerTitle!, depth: 2})} + ${renderer.parser.renderer.heading(token.header)} ${illustration}
    diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-block.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-block.mts index ddab5b863fdc..75678a67deeb 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-block.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-block.mts @@ -16,6 +16,7 @@ export interface DocsCodeBlock extends CodeToken { code: string; // Code language language: string | undefined; + style: 'prefer' | 'avoid' | undefined; } /** @@ -39,6 +40,9 @@ export const docsCodeBlockExtension = { const headerRule = /header\s*:\s*(['"`])([^'"`]+)\1/; // The 2nd capture matters here const highlightRule = /highlight\s*:\s*(.*)([^,])/; + const hideCopyRule = /hideCopy/; + const preferRule = /\b(prefer|avoid)\b/; + const linenumsRule = /linenums/; const token: DocsCodeBlock = { raw: match[0], @@ -47,6 +51,9 @@ export const docsCodeBlockExtension = { language: match[1], header: headerRule.exec(metadataStr)?.[2], highlight: highlightRule.exec(metadataStr)?.[1], + hideCopy: hideCopyRule.test(metadataStr), + style: preferRule.exec(metadataStr)?.[1] as 'prefer' | 'avoid' | undefined, + linenums: linenumsRule.test(metadataStr), }; return token; } @@ -65,7 +72,6 @@ export const docsCodeBlockExtension = { */ function deindent(str: string): string { const lines = str.split('\n'); - const nonEmpty = lines.filter((line) => line.trim()); let minIndent = Infinity; for (const line of lines) { if (!line.trim()) { diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts index e1d50efbf375..2bb5797f07d0 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts @@ -29,9 +29,10 @@ const linenumsRule = /linenums/; const highlightRule = /highlight="([^"]*)"/; const languageRule = /language="([^"]*)"/; const visibleLinesRule = /visibleLines="([^"]*)"/; -const visibleRegionRule = /visibleRegion="([^"]*)"/; +const regionRule = /region="([^"]*)"/; const previewRule = /preview/; const hideCodeRule = /hideCode/; +const preferRule = /\b(prefer|avoid)\b/; export const docsCodeExtension = { name: 'docs-code', @@ -50,10 +51,11 @@ export const docsCodeExtension = { const highlight = highlightRule.exec(attr); const language = languageRule.exec(attr); const visibleLines = visibleLinesRule.exec(attr); - const visibleRegion = visibleRegionRule.exec(attr); + const region = regionRule.exec(attr); const preview = previewRule.exec(attr) ? true : false; const hideCode = hideCodeRule.exec(attr) ? true : false; const classes = classRule.exec(attr); + const style = preferRule.exec(attr); let code = match[2]?.trim() ?? ''; if (path && path[1]) { @@ -73,9 +75,10 @@ export const docsCodeExtension = { highlight: highlight?.[1], language: language?.[1], visibleLines: visibleLines?.[1], - visibleRegion: visibleRegion?.[1], + region: region?.[1], preview: preview, hideCode, + style: style?.[1] as 'prefer' | 'avoid' | undefined, classes: classes?.[1]?.split(' '), }; return token; diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts index bb620f98fef9..84cad10c9d32 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts @@ -29,29 +29,31 @@ export interface CodeToken extends Tokens.Generic { linenums?: boolean; /* The lines viewable in collapsed view */ visibleLines?: string; - /* The name of the viewable region in the collapsed view */ - visibleRegion?: string; + /* The name of the region to show in the code snippet */ + region?: string; /* Whether we should display preview */ preview?: boolean; /** Whether to hide code example by default. */ hideCode?: boolean; /* The lines to display highlighting on */ highlight?: string; + /** Whether to hide the copy button */ + hideCopy?: boolean; // additional classes for the element classes?: string[]; } export function formatCode(token: CodeToken, context: RendererContext): string { - if (token.visibleLines !== undefined && token.visibleRegion !== undefined) { - throw Error('Cannot define visible lines and visible region at the same time'); + if (token.visibleLines !== undefined && token.region !== undefined) { + throw Error('Cannot define visible lines and region at the same time'); } extractRegions(token); highlightCode(context.highlighter, token); const containerEl = JSDOM.fragment(` -
    +
    ${buildHeaderElement(token)} ${token.code}
    @@ -93,7 +95,18 @@ export function processForApiLinks(fragment: Element, apiEntries: ApiEntries): v /** Build the header element if a header is provided in the token. */ function buildHeaderElement(token: CodeToken) { - return token.header ? `

    ${token.header}

    ` : ''; + let header = ''; + if (token.style) { + header += `${token.style === 'prefer' ? 'Prefer' : 'Avoid'}`; + } + + if (token.header) { + header += `

    ${token.header}

    `; + } + + if (!header) return ''; + + return `
    ${header}
    `; } function applyContainerAttributesAndClasses(el: Element, token: CodeToken) { @@ -116,7 +129,9 @@ function applyContainerAttributesAndClasses(el: Element, token: CodeToken) { if (token.hideCode) { el.setAttribute('hideCode', 'true'); } - + if (token.hideCopy) { + el.setAttribute('hideCopy', 'true'); + } const language = token.language; if (language === 'mermaid') { diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/region.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/region.mts index e47d5e5afe1a..e219d60c838f 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/region.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/region.mts @@ -19,13 +19,11 @@ export function extractRegions(token: CodeToken) { // The code in the token is always replaced with the version of the code with region markers removed. token.code = parsedRegions.contents; - if (token.visibleRegion) { - const region = parsedRegions.regionMap[token.visibleRegion]; + if (token.region) { + const region = parsedRegions.regionMap[token.region]; if (!region) { - throw new Error(`Cannot find ${token.visibleRegion} in ${token.path}!`); + throw new Error(`Cannot find ${token.region} in ${token.path}!`); } - token.visibleLines = `[${region.ranges.map( - (range) => `[${range.from}, ${range.to ?? parsedRegions.totalLinesCount + 1}]`, - )}]`; + token.code = region.lines.join('\n').trim(); } } diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts index 5695f9acb08d..486be3492837 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts @@ -9,7 +9,6 @@ import {TokenizerThis, Tokens, RendererThis} from 'marked'; import {loadWorkspaceRelativeFile} from '../helpers.mjs'; import {getPageTitle} from '../transformations/heading.mjs'; -import {AdevDocsRenderer} from '../renderer.mjs'; interface DocsDecorativeHeaderToken extends Tokens.Generic { type: 'docs-decorative-header'; diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-workflow/docs-step.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-workflow/docs-step.mts index 85ed39278bdb..514a3f816793 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-workflow/docs-step.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-workflow/docs-step.mts @@ -7,14 +7,12 @@ */ import {Token, Tokens, RendererThis, TokenizerThis} from 'marked'; -import {formatHeading} from '../../transformations/heading.mjs'; -import {AdevDocsRenderer} from '../../renderer.mjs'; interface DocsStepToken extends Tokens.Generic { type: 'docs-step'; - title: string; body: string; tokens: Token[]; + header: Tokens.Heading; } // Capture group 1: all attributes on the opening tag @@ -35,13 +33,20 @@ export const docsStepExtension = { const attr = match[1].trim(); const title = titleRule.exec(attr); const body = match[2].trim(); + const header = title ? title[1] : ''; const token: DocsStepToken = { type: 'docs-step', raw: match[0], - title: title ? title[1] : '', body: body, tokens: [], + header: { + text: header, + raw: header, + tokens: this.lexer.inlineTokens(header, []), + type: 'heading', + depth: 3, + }, }; this.lexer.blockTokens(token.body, token.tokens); return token; @@ -52,7 +57,7 @@ export const docsStepExtension = { return `
  • - ${formatHeading({text: token.title, depth: 3}, (this.parser.renderer as AdevDocsRenderer).context.markdownFilePath)} + ${this.parser.renderer.heading(token.header)} ${this.parser.parse(token.tokens)}
  • `; diff --git a/adev/shared-docs/pipeline/shared/marked/hooks.mts b/adev/shared-docs/pipeline/shared/marked/hooks.mts deleted file mode 100644 index f85deae66e02..000000000000 --- a/adev/shared-docs/pipeline/shared/marked/hooks.mts +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {MarkedExtension} from 'marked'; -import {resetHeaderIdsOfCurrentDocument} from './state.mjs'; - -/** - * Custom hooks for marked that will be used to post-transform markdown files with parent styles for docs. - */ -export const hooks: MarkedExtension['hooks'] = { - preprocess(html: string): string { - resetHeaderIdsOfCurrentDocument(); - return html; - }, - postprocess(html: string): string { - return html; - }, -}; diff --git a/adev/shared-docs/pipeline/shared/marked/parse.mts b/adev/shared-docs/pipeline/shared/marked/parse.mts index dd5c2c597625..c1301fe970f7 100644 --- a/adev/shared-docs/pipeline/shared/marked/parse.mts +++ b/adev/shared-docs/pipeline/shared/marked/parse.mts @@ -25,8 +25,6 @@ import {docsCodeMultifileExtension} from './extensions/docs-code/docs-code-multi import {docsTabGroupExtension, docsTabExtension} from './extensions/docs-tabs.mjs'; import {docsImageExtension} from './extensions/docs-image.mjs'; -import {hooks} from './hooks.mjs'; - let markedInstance: typeof marked; const extensions = [ docsImageExtension, @@ -51,14 +49,14 @@ const extensions = [ export async function parseMarkdownAsync( markdownContent: string, - context: RendererContext, + context: Partial, ): Promise { - markedInstance ??= marked.use({hooks, extensions, walkTokens, async: true}); + markedInstance ??= marked.use({extensions, walkTokens, async: true}); return markedInstance.parse(markdownContent, {renderer: new AdevDocsRenderer(context)}); } -export function parseMarkdown(markdownContent: string, context: RendererContext): string { - markedInstance ??= marked.use({hooks, extensions, walkTokens}); +export function parseMarkdown(markdownContent: string, context: Partial): string { + markedInstance ??= marked.use({extensions, walkTokens}); return markedInstance.parse(markdownContent, {renderer: new AdevDocsRenderer(context)}) as string; } diff --git a/adev/shared-docs/pipeline/shared/marked/renderer.mts b/adev/shared-docs/pipeline/shared/marked/renderer.mts index fa86c2346bc6..b76160f702db 100644 --- a/adev/shared-docs/pipeline/shared/marked/renderer.mts +++ b/adev/shared-docs/pipeline/shared/marked/renderer.mts @@ -18,8 +18,9 @@ import {HighlighterGeneric} from 'shiki'; export interface RendererContext { markdownFilePath?: string; - apiEntries?: Record; + apiEntries?: Record; highlighter: HighlighterGeneric; + headerIds: Map; } /** @@ -27,12 +28,24 @@ export interface RendererContext { * files that can be used in the Angular docs. */ export class AdevDocsRenderer extends Renderer { - constructor(public context: RendererContext) { + public context: RendererContext; + constructor(context: Partial) { super(); + if (context.highlighter === undefined) { + throw Error( + 'An instance of HighlighterGeneric must be provided to the AdevDocsRenderer at construction', + ); + } + context.headerIds = context.headerIds || new Map(); + this.context = context as RendererContext; } defaultRenderer = new Renderer(); + isGuideFile(): boolean { + return this.context.markdownFilePath?.includes('/content/guide') ?? false; + } + override link = linkRender; override table = tableRender; override list = listRender; @@ -40,4 +53,34 @@ export class AdevDocsRenderer extends Renderer { override text = textRender; override heading = headingRender; override codespan = codespanRender; + + getHeaderId(heading: string) { + const numberOfHeaderOccurrencesInTheDocument = this.context.headerIds.get(heading) ?? 0; + this.context.headerIds.set(heading, numberOfHeaderOccurrencesInTheDocument + 1); + + // extract the extended markdown heading id + // ex: ## MyHeading {# myId} + const match = heading.match(/{#([\w-]+)}/); + + let extractedId: string; + if (match) { + extractedId = match[1]; + } else { + extractedId = heading + .toLowerCase() + .replace(/(.*?)<\/code>/g, '$1') // remove + .replace(/(.*?)<\/strong>/g, '$1') // remove + .replace(/(.*?)<\/em>/g, '$1') // remove + .replace(/\s|\//g, '-') // remove spaces and slashes + .replace(/gt;|lt;/g, '') // remove escaped < and > + .replace(/&#\d+;/g, '') // remove HTML entities + .replace(/[^\p{L}\d\-]/gu, ''); // only keep letters, digits & dashes + } + + const headerId = numberOfHeaderOccurrencesInTheDocument + ? `${extractedId}-${numberOfHeaderOccurrencesInTheDocument}` + : extractedId; + + return headerId; + } } diff --git a/adev/shared-docs/pipeline/shared/marked/state.mts b/adev/shared-docs/pipeline/shared/marked/state.mts deleted file mode 100644 index 988f8004d95a..000000000000 --- a/adev/shared-docs/pipeline/shared/marked/state.mts +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const headerIds = new Map(); - -export const getHeaderId = (heading: string): string => { - const numberOfHeaderOccurrencesInTheDocument = headerIds.get(heading) ?? 0; - headerIds.set(heading, numberOfHeaderOccurrencesInTheDocument + 1); - - // extract the extended markdown heading id - // ex: ## MyHeading {# myId} - const match = heading.match(/{#([\w-]+)}/); - - let extractedId: string; - if (match) { - extractedId = match[1]; - } else { - extractedId = heading - .toLowerCase() - .replace(/(.*?)<\/code>/g, '$1') // remove - .replace(/(.*?)<\/strong>/g, '$1') // remove - .replace(/(.*?)<\/em>/g, '$1') // remove - .replace(/\s|\//g, '-') // remove spaces and slashes - .replace(/gt;|lt;/g, '') // remove escaped < and > - .replace(/&#\d+;/g, '') // remove HTML entities - .replace(/[^\p{L}\d\-]/gu, ''); // only keep letters, digits & dashes - } - - const headerId = numberOfHeaderOccurrencesInTheDocument - ? `${extractedId}-${numberOfHeaderOccurrencesInTheDocument}` - : extractedId; - - return headerId; -}; - -export const resetHeaderIdsOfCurrentDocument = (): void => { - headerIds.clear(); -}; diff --git a/adev/shared-docs/pipeline/shared/marked/test/docs-code/BUILD.bazel b/adev/shared-docs/pipeline/shared/marked/test/docs-code/BUILD.bazel index ae6676f661bf..f36de49057ac 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/docs-code/BUILD.bazel +++ b/adev/shared-docs/pipeline/shared/marked/test/docs-code/BUILD.bazel @@ -20,7 +20,7 @@ zoneless_jasmine_test( "docs-code.md", "example-with-eslint-comment.ts", "example-with-region.ts", - "messages.fr.xlf.html", + "messages.fr.xlf", "new-code.ts", "old-code.ts", ":docs-code", diff --git a/adev/shared-docs/pipeline/shared/marked/test/docs-code/docs-code.md b/adev/shared-docs/pipeline/shared/marked/test/docs-code/docs-code.md index 4ee6575df1ce..73871835de4c 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/docs-code/docs-code.md +++ b/adev/shared-docs/pipeline/shared/marked/test/docs-code/docs-code.md @@ -5,4 +5,4 @@ this is code - + diff --git a/adev/shared-docs/pipeline/shared/marked/test/docs-code/messages.fr.xlf.html b/adev/shared-docs/pipeline/shared/marked/test/docs-code/messages.fr.xlf similarity index 100% rename from adev/shared-docs/pipeline/shared/marked/test/docs-code/messages.fr.xlf.html rename to adev/shared-docs/pipeline/shared/marked/test/docs-code/messages.fr.xlf diff --git a/adev/shared-docs/pipeline/shared/marked/test/link/link.md b/adev/shared-docs/pipeline/shared/marked/test/link/link.md index a156adc3a6c8..e8a6b8287963 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/link/link.md +++ b/adev/shared-docs/pipeline/shared/marked/test/link/link.md @@ -1,4 +1,4 @@ -[Angular Site](https://angular.dev) +[Please click](https://www.youtube.com/watch?v=dQw4w9WgXcQ) [same page](#test) [same site](../other/page) [npm packages](https://docs.npmjs.com/getting-started/what-is-npm 'What is npm?') diff --git a/adev/shared-docs/pipeline/shared/marked/test/link/link.spec.mts b/adev/shared-docs/pipeline/shared/marked/test/link/link.spec.mts index 7ad4a1e6b2ed..cc4f753f2b6b 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/link/link.spec.mts +++ b/adev/shared-docs/pipeline/shared/marked/test/link/link.spec.mts @@ -20,7 +20,7 @@ describe('markdown to html', () => { it('should render external links with _blank target', () => { expect(parsedMarkdown).toContain( - 'Angular Site', + 'Please click', ); }); @@ -37,4 +37,17 @@ describe('markdown to html', () => { 'npm packages', ); }); + + it('should throw if on absolute links to adev', async () => { + try { + parsedMarkdown = await parseMarkdown( + '[Some absolute link](https://angular.dev/yeah-nope-should-be-relative)', + {...rendererContext, markdownFilePath: '/content/guide/some-guide.md'}, + ); + } catch (e: any) { + expect(e.message).toContain('Absolute links to angular.dev are not allowed'); + return; + } + fail('Did not throw for absolute link to angular.dev'); + }); }); diff --git a/adev/shared-docs/pipeline/shared/marked/test/list/list.md b/adev/shared-docs/pipeline/shared/marked/test/list/list.md index 6c060aaa619a..6bd5e4eef200 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/list/list.md +++ b/adev/shared-docs/pipeline/shared/marked/test/list/list.md @@ -10,5 +10,5 @@ - here - matter - doesn't -- [some link](https://angular.dev) +- [some link](https://www.youtube.com/watch?v=dQw4w9WgXcQ) - Code block `SomeClass` diff --git a/adev/shared-docs/pipeline/shared/marked/test/list/list.spec.mts b/adev/shared-docs/pipeline/shared/marked/test/list/list.spec.mts index cbf5f2aa5e1e..bc65c5c03308 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/list/list.spec.mts +++ b/adev/shared-docs/pipeline/shared/marked/test/list/list.spec.mts @@ -35,7 +35,7 @@ describe('markdown to html', () => { it('should render list items', () => { const unorderedList = markdownDocument.querySelector('ul'); const linkItem = unorderedList!.children[4]; - expect(linkItem.outerHTML).toContain('href="https://angular.dev"'); + expect(linkItem.outerHTML).toContain('href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"'); const codeItem = unorderedList!.children[5]; expect(codeItem.outerHTML).toContain('SomeClass'); diff --git a/adev/shared-docs/pipeline/shared/marked/test/mermaid/BUILD.bazel b/adev/shared-docs/pipeline/shared/marked/test/mermaid/BUILD.bazel index 9adf39f961d0..81216bf86409 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/mermaid/BUILD.bazel +++ b/adev/shared-docs/pipeline/shared/marked/test/mermaid/BUILD.bazel @@ -20,11 +20,7 @@ zoneless_jasmine_test( data = [ ":unit_test_lib", "@rules_browsers//browsers/chromium", - ] + glob([ - "**/*.md", - "**/*.svg", - "**/*.mts", - ]), + ], env = { "CHROME_HEADLESS_BIN": "$(CHROME-HEADLESS-SHELL)", }, diff --git a/adev/shared-docs/pipeline/shared/marked/test/renderer-context.mts b/adev/shared-docs/pipeline/shared/marked/test/renderer-context.mts index 2d1c2870f3ba..dfaf5ce1d978 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/renderer-context.mts +++ b/adev/shared-docs/pipeline/shared/marked/test/renderer-context.mts @@ -19,11 +19,12 @@ export const rendererContext: RendererContext = { markdownFilePath: '', highlighter: null!, apiEntries: { - CommonModule: 'angular/common', - bootstrapApplication: 'angular/platform-browser', - ApplicationRef: 'angular/core', - Router: 'angular/router', + CommonModule: {moduleName: 'angular/common'}, + bootstrapApplication: {moduleName: 'angular/platform-browser'}, + ApplicationRef: {moduleName: 'angular/core'}, + Router: {moduleName: 'angular/router'}, }, + headerIds: new Map(), }; export async function setHighlighter() { diff --git a/adev/shared-docs/pipeline/shared/marked/transformations/heading.mts b/adev/shared-docs/pipeline/shared/marked/transformations/heading.mts index e8655b0cca17..2dbcba2cae39 100644 --- a/adev/shared-docs/pipeline/shared/marked/transformations/heading.mts +++ b/adev/shared-docs/pipeline/shared/marked/transformations/heading.mts @@ -7,24 +7,15 @@ */ import {Tokens} from 'marked'; -import {getHeaderId} from '../state.mjs'; import {AdevDocsRenderer} from '../renderer.mjs'; export function headingRender(this: AdevDocsRenderer, {depth, tokens}: Tokens.Heading): string { const text = this?.parser.parseInline(tokens); - return formatHeading({text, depth}, this.context.markdownFilePath); -} - -export function formatHeading( - {text, depth}: {text: string; depth: number}, - markdownFilePath?: string, -): string { if (depth === 1) { return `
    - - ${getPageTitle(text, markdownFilePath)} + ${getPageTitle(text, this.context.markdownFilePath)}
    `; } @@ -39,12 +30,13 @@ export function formatHeading( // ex: ## MyHeading {# myId} const customIdRegex = /{#\s*([\w-]+)\s*}/g; const customId = customIdRegex.exec(anchorLessText)?.[1]; - const link = customId ?? getHeaderId(anchorLessText); + const link = customId ?? this.getHeaderId(anchorLessText); const label = anchorLessText.replace(/`(.*?)`/g, '$1').replace(customIdRegex, ''); + const normalizedLabel = label.replace(/<\/?code>/g, ''); return ` - ${label} + ${label} `; } diff --git a/adev/shared-docs/pipeline/shared/marked/transformations/link.mts b/adev/shared-docs/pipeline/shared/marked/transformations/link.mts index 4b196327d7c4..d5d176e2ebfc 100644 --- a/adev/shared-docs/pipeline/shared/marked/transformations/link.mts +++ b/adev/shared-docs/pipeline/shared/marked/transformations/link.mts @@ -8,6 +8,7 @@ import {anchorTarget} from '../helpers.mjs'; import {Renderer, Tokens} from 'marked'; +import {AdevDocsRenderer} from '../renderer.mjs'; /** * Tracks whether the current renderer is inside a link. @@ -19,7 +20,20 @@ export function setInsideLink(value: boolean) { insideLink = value; } -export function linkRender(this: Renderer, {href, title, tokens}: Tokens.Link) { +export function linkRender(this: AdevDocsRenderer, {href, title, tokens}: Tokens.Link) { + // We have render-time check that we don't create absolute links (which are rendered as external links) + // in our guides + if ( + (href.startsWith('https://angular.dev/') || href.startsWith('http://angular.dev/')) && + this.isGuideFile() + ) { + Error.stackTraceLimit = Infinity; + throw new Error( + `Absolute links to angular.dev are not allowed: "${href}". Please use relative links instead.` + + `\n ----------------------------- \n ${(this as any).__raw}`, + ); + } + if (insideLink) { return this.parser.parseInline(tokens); } diff --git a/adev/shared-docs/pipeline/shared/test/linking.spec.mts b/adev/shared-docs/pipeline/shared/test/linking.spec.mts new file mode 100644 index 000000000000..f0fd0b02536a --- /dev/null +++ b/adev/shared-docs/pipeline/shared/test/linking.spec.mts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {getSymbolUrl} from '../linking.mjs'; + +describe('getSymbolUrl', () => { + it('should resolve class names to API URLs', () => { + const apiEntries = { + Combobox: {moduleName: 'aria/combobox'}, + AccordionPanel: {moduleName: 'aria/accordion'}, + Grid: {moduleName: 'aria/grid'}, + }; + + expect(getSymbolUrl('Combobox', apiEntries)).toBe('/api/aria/combobox/Combobox'); + expect(getSymbolUrl('AccordionPanel', apiEntries)).toBe('/api/aria/accordion/AccordionPanel'); + expect(getSymbolUrl('Grid', apiEntries)).toBe('/api/aria/grid/Grid'); + }); + + it('should resolve selector aliases to class names', () => { + const apiEntries = { + Combobox: {moduleName: 'aria/combobox'}, + ngCombobox: {moduleName: 'aria/combobox', targetSymbol: 'Combobox'}, + AccordionPanel: {moduleName: 'aria/accordion'}, + ngAccordionPanel: {moduleName: 'aria/accordion', targetSymbol: 'AccordionPanel'}, + GridCell: {moduleName: 'aria/grid'}, + ngGridCell: {moduleName: 'aria/grid', targetSymbol: 'GridCell'}, + }; + + expect(getSymbolUrl('ngCombobox', apiEntries)).toBe('/api/aria/combobox/Combobox'); + expect(getSymbolUrl('ngAccordionPanel', apiEntries)).toBe('/api/aria/accordion/AccordionPanel'); + expect(getSymbolUrl('ngGridCell', apiEntries)).toBe('/api/aria/grid/GridCell'); + }); + + it('should handle selector aliases with properties', () => { + const apiEntries = { + AccordionPanel: {moduleName: 'aria/accordion'}, + ngAccordionPanel: {moduleName: 'aria/accordion', targetSymbol: 'AccordionPanel'}, + ComboboxInput: {moduleName: 'aria/combobox'}, + ngComboboxInput: {moduleName: 'aria/combobox', targetSymbol: 'ComboboxInput'}, + }; + + expect(getSymbolUrl('ngAccordionPanel.visible', apiEntries)).toBe( + '/api/aria/accordion/AccordionPanel#visible', + ); + expect(getSymbolUrl('ngComboboxInput.value', apiEntries)).toBe( + '/api/aria/combobox/ComboboxInput#value', + ); + }); + + it('should return undefined for unknown symbols', () => { + const apiEntries = { + AccordionPanel: {moduleName: 'aria/accordion'}, + ngAccordionPanel: {moduleName: 'aria/accordion', targetSymbol: 'AccordionPanel'}, + }; + + expect(getSymbolUrl('UnknownSymbol', apiEntries)).toBeUndefined(); + expect(getSymbolUrl('unknownSelector', apiEntries)).toBeUndefined(); + expect(getSymbolUrl('ngUnknownDirective', apiEntries)).toBeUndefined(); + }); +}); diff --git a/adev/shared-docs/pipeline/tutorials/common/src/index.html b/adev/shared-docs/pipeline/tutorials/common/src/index.html index 95e7cf90822c..53447dbe4ccc 100644 --- a/adev/shared-docs/pipeline/tutorials/common/src/index.html +++ b/adev/shared-docs/pipeline/tutorials/common/src/index.html @@ -1,4 +1,4 @@ - + diff --git a/adev/shared-docs/pipeline/tutorials/common/src/styles.css b/adev/shared-docs/pipeline/tutorials/common/src/styles.css index 2a37a8dfc705..c366f1a33d28 100644 --- a/adev/shared-docs/pipeline/tutorials/common/src/styles.css +++ b/adev/shared-docs/pipeline/tutorials/common/src/styles.css @@ -1,4 +1,18 @@ /* You can add global styles to this file, and also import other style files */ +:root { + --primary-color: #605dc8; + --secondary-color: #8b89e6; + --accent-color: #e8e7fa; + --shadow-color: #e8e8e8; +} + +button.primary { + padding: 10px; + border: solid 1px var(--primary-color); + background: var(--primary-color); + color: white; + border-radius: 8px; +} body { font-family: 'Be Vietnam Pro', sans-serif; -} \ No newline at end of file +} diff --git a/adev/shared-docs/pipeline/tutorials/playground_index.mts b/adev/shared-docs/pipeline/tutorials/playground_index.mts index 8784ca268e1f..242166095373 100644 --- a/adev/shared-docs/pipeline/tutorials/playground_index.mts +++ b/adev/shared-docs/pipeline/tutorials/playground_index.mts @@ -57,7 +57,7 @@ async function generatePlaygroundFiles( ); writeFileSync( join(outputDir, path, 'source-code.json'), - JSON.stringify(await generateSourceCode(config, itemFiles)), + JSON.stringify(await generateSourceCode(itemFiles)), ); } diff --git a/adev/shared-docs/pipeline/tutorials/source-code.mts b/adev/shared-docs/pipeline/tutorials/source-code.mts index 5de2314dc875..27a19265a168 100644 --- a/adev/shared-docs/pipeline/tutorials/source-code.mts +++ b/adev/shared-docs/pipeline/tutorials/source-code.mts @@ -7,14 +7,11 @@ */ import {FileSystemTree} from '@webcontainer/api'; -import {FileAndContentRecord, TutorialConfig} from '../../interfaces/index'; +import {FileAndContentRecord} from '../../interfaces/index'; import {getFileSystemTree} from './webcontainers.mjs'; /** Generate the source-code.json content for a provided tutorial config. */ -export async function generateSourceCode( - config: TutorialConfig, - files: FileAndContentRecord, -): Promise { +export async function generateSourceCode(files: FileAndContentRecord): Promise { // TODO(josephperrott): figure out if filtering is needed for this. const allFiles = Object.keys(files); return getFileSystemTree(allFiles, files); diff --git a/adev/shared-docs/pipeline/tutorials/tutorial_index.mts b/adev/shared-docs/pipeline/tutorials/tutorial_index.mts index c2024c679c33..3f034c77024f 100644 --- a/adev/shared-docs/pipeline/tutorials/tutorial_index.mts +++ b/adev/shared-docs/pipeline/tutorials/tutorial_index.mts @@ -54,7 +54,7 @@ async function generateTutorialFiles(tutorialDir: string, commonDir: string, out ); writeFileSync( join(outputDir, 'source-code.json'), - JSON.stringify(await generateSourceCode(introConfig, introFiles)), + JSON.stringify(await generateSourceCode(introFiles)), ); // For each tutorial step, generate the metadata and source-code files. @@ -75,7 +75,7 @@ async function generateTutorialFiles(tutorialDir: string, commonDir: string, out ); writeFileSync( join(outputDir, path, 'source-code.json'), - JSON.stringify(await generateSourceCode(config, itemFiles)), + JSON.stringify(await generateSourceCode(itemFiles)), ); } diff --git a/adev/shared-docs/services/search.service.ts b/adev/shared-docs/services/search.service.ts index aaac5242471d..96df876a74e5 100644 --- a/adev/shared-docs/services/search.service.ts +++ b/adev/shared-docs/services/search.service.ts @@ -53,47 +53,7 @@ export class Search { loader: async ({params: query, abortSignal}) => { // Until we have a better alternative we debounce by awaiting for a short delay. await wait(SEARCH_DELAY, abortSignal); - - return this.client - .search([ - { - indexName: this.config.algolia.indexName, - params: { - query: query, - maxValuesPerFacet: MAX_VALUE_PER_FACET, - attributesToRetrieve: [ - 'hierarchy.lvl0', - 'hierarchy.lvl1', - 'hierarchy.lvl2', - 'hierarchy.lvl3', - 'hierarchy.lvl4', - 'hierarchy.lvl5', - 'hierarchy.lvl6', - 'content', - 'type', - 'url', - ], - hitsPerPage: 20, - snippetEllipsisText: '…', - highlightPreTag: '<ɵ>', - highlightPostTag: '', - attributesToHighlight: [], - attributesToSnippet: [ - 'hierarchy.lvl1:10', - 'hierarchy.lvl2:10', - 'hierarchy.lvl3:10', - 'hierarchy.lvl4:10', - 'hierarchy.lvl5:10', - 'hierarchy.lvl6:10', - 'content:10', - ], - }, - type: 'default', - }, - ]) - .then((response: SearchResponses) => { - return this.parseResult(response); - }); + return this.searchWithQuery(query); }, }); @@ -208,6 +168,49 @@ export class Search { }) .join(''); } + + public searchWithQuery(query: string): Promise { + return this.client + .search([ + { + indexName: this.config.algolia.indexName, + params: { + query: query, + maxValuesPerFacet: MAX_VALUE_PER_FACET, + attributesToRetrieve: [ + 'hierarchy.lvl0', + 'hierarchy.lvl1', + 'hierarchy.lvl2', + 'hierarchy.lvl3', + 'hierarchy.lvl4', + 'hierarchy.lvl5', + 'hierarchy.lvl6', + 'content', + 'type', + 'url', + ], + hitsPerPage: 20, + snippetEllipsisText: '…', + highlightPreTag: '<ɵ>', + highlightPostTag: '', + attributesToHighlight: [], + attributesToSnippet: [ + 'hierarchy.lvl1:10', + 'hierarchy.lvl2:10', + 'hierarchy.lvl3:10', + 'hierarchy.lvl4:10', + 'hierarchy.lvl5:10', + 'hierarchy.lvl6:10', + 'content:10', + ], + }, + type: 'default', + }, + ]) + .then((response: SearchResponses) => { + return this.parseResult(response); + }); + } } function matched(snippet: SnippetResult | undefined): boolean { diff --git a/adev/shared-docs/services/table-of-contents-loader.service.ts b/adev/shared-docs/services/table-of-contents-loader.service.ts index de84933cd793..a67c8a2fd496 100644 --- a/adev/shared-docs/services/table-of-contents-loader.service.ts +++ b/adev/shared-docs/services/table-of-contents-loader.service.ts @@ -44,7 +44,10 @@ export class TableOfContentsLoader { private getHeadingTitle(heading: HTMLHeadingElement): string { const div: HTMLDivElement = this.document.createElement('div'); + div.innerHTML = heading.innerHTML; + // Remove any existing copy link buttons to avoid including their text in the title + div.querySelectorAll('docs-copy-link-button').forEach((el) => el.remove()); return (div.textContent || '').trim(); } diff --git a/adev/shared-docs/styles/_anchor.scss b/adev/shared-docs/styles/_anchor.scss deleted file mode 100644 index 209853531899..000000000000 --- a/adev/shared-docs/styles/_anchor.scss +++ /dev/null @@ -1,18 +0,0 @@ -@mixin docs-anchor() { - &::after { - content: '\e157'; // codepoint for "link" - font-family: 'Material Symbols Outlined'; - opacity: 0; - margin-left: 8px; - vertical-align: middle; - color: var(--quaternary-contrast); - font-size: clamp(18px, 1.25em, 30px); - transition: opacity 0.3s ease; - } - - &:hover { - &::after { - opacity: 1; - } - } -} diff --git a/adev/shared-docs/styles/_reference.scss b/adev/shared-docs/styles/_reference.scss index 2f0ce55c6e4d..1d3f7381966c 100644 --- a/adev/shared-docs/styles/_reference.scss +++ b/adev/shared-docs/styles/_reference.scss @@ -1,5 +1,3 @@ -@use './anchor' as anchor; - /* Common styles for the API & CLI references */ @mixin reference-common() { .docs-code { @@ -71,11 +69,24 @@ } & > a { - @include anchor.docs-anchor(); color: inherit; } } + .docs-api { + h1, + h2, + h3, + h4, + h5, + h6 { + // Show copy link button when hovering the heading + &:hover docs-copy-link-button { + opacity: 1; + } + } + } + .docs-reference-members { box-sizing: border-box; width: 100%; @@ -336,6 +347,16 @@ border-block-start: 1px solid var(--senary-contrast); } } + + .docs-usage-notes { + border-block-start: 1px solid var(--senary-contrast); + + .docs-usage-notes-heading { + color: var(--primary-contrast); + margin: 0.5rem 0; + font-size: 1.2rem; + } + } } } } diff --git a/adev/shared-docs/styles/docs/_code.scss b/adev/shared-docs/styles/docs/_code.scss index 4147aa85fe40..556318e4fed3 100644 --- a/adev/shared-docs/styles/docs/_code.scss +++ b/adev/shared-docs/styles/docs/_code.scss @@ -166,6 +166,18 @@ $code-font-size: 0.875rem; border-width: 0; border-radius: 0; } + + &.docs-code-avoid { + --style-color: var(--hot-red); + } + + &.docs-code-prefer { + --style-color: var(--symbolic-green); + } + &.docs-code-avoid, + &.docs-code-prefer { + border-color: color-mix(in srgb, var(--style-color) 40%, var(--senary-contrast)); + } } .shiki { @@ -278,18 +290,51 @@ $code-font-size: 0.875rem; } .docs-code-header { + display: flex; padding: 0.75rem; - // docs header background - &::before { - content: ''; - position: absolute; - inset: 0; - width: 100%; - height: 100%; - background: var(--subtle-purple); - border-radius: 0.25rem 0.25rem 0 0; - transition: background 0.3s ease; + background: var(--subtle-purple); + border-radius: 0.25rem 0.25rem 0 0; + transition: background 0.3s ease; + + .docs-code-avoid &, + .docs-code-prefer & { + background: color-mix(in srgb, var(--style-color) 17%, var(--page-background)); + color: color-mix(in srgb, var(--style-color) 60%, var(--primary-contrast)); + h3 { + color: color-mix(in srgb, var(--style-color) 60%, var(--primary-contrast)); + } + } + + .docs-code-header-style { + display: flex; + align-items: center; + font-size: 0.8rem; + + &::before { + font-family: var(--icons); + margin-right: 0.5rem; + } + &:not(:last-child)::after { + content: '-'; + margin: 0 0.5rem; + } + } + + .docs-code-header-style { + .docs-code-prefer & { + &::before { + content: 'check'; + } + } + } + + .docs-code-header-style { + .docs-code-avoid & { + &::before { + content: 'dangerous'; + } + } } } @@ -373,6 +418,11 @@ $code-font-size: 0.875rem; display: none; } + // Hide the last line if it's empty + &:last-child:empty { + display: none; + } + span:last-child { margin-inline-end: 4rem; } diff --git a/adev/shared-docs/testing/testing-helper.ts b/adev/shared-docs/testing/testing-helper.ts index d8fcbd7c2b38..36cb00702420 100644 --- a/adev/shared-docs/testing/testing-helper.ts +++ b/adev/shared-docs/testing/testing-helper.ts @@ -111,6 +111,8 @@ export class FakeWebContainer extends WebContainer { override teardown() {} override fs: FakeFileSystemAPI = new FakeFileSystemAPI(); + + override async setPreviewScript(script: string): Promise {} } class FakeFileSystemAPI implements FileSystemAPI { diff --git a/adev/src/app/app.component.html b/adev/src/app/app.component.html index 5977c3aa81d7..dd6f8244493d 100644 --- a/adev/src/app/app.component.html +++ b/adev/src/app/app.component.html @@ -2,7 +2,12 @@ @defer (when isBrowser) { - + }
    diff --git a/adev/src/app/core/layout/footer/footer.component.html b/adev/src/app/core/layout/footer/footer.component.html index 2811dd24d352..a12af57fa57b 100644 --- a/adev/src/app/core/layout/footer/footer.component.html +++ b/adev/src/app/core/layout/footer/footer.component.html @@ -115,6 +115,6 @@

    Languages

    MIT-style License . Documentation licensed under CC BY 4.0 - . Built by Angular at v{{angularVersion}}. + . Built by Angular at v{{ angularVersion }}.

    diff --git a/adev/src/app/core/layout/navigation/navigation.component.html b/adev/src/app/core/layout/navigation/navigation.component.html index 8786877d73c5..238037bc7079 100644 --- a/adev/src/app/core/layout/navigation/navigation.component.html +++ b/adev/src/app/core/layout/navigation/navigation.component.html @@ -172,16 +172,11 @@ diff --git a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.html b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.html index 94da55f60367..779e8027f0a7 100644 --- a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.html +++ b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.html @@ -1,5 +1,15 @@ -
    -
    +
    +
    @if (navigationItems && navigationItems.length > 0) { `, imports: [MatSnackBarAction], - styles: `:host { display: flex; align-items: center; button { margin-left: 16px }}`, + styles: ` + :host { + display: flex; + align-items: center; + button { + margin-left: 16px; + } + } + `, }) export class ErrorSnackBar { protected snackBarRef = inject>(MatSnackBarRef); diff --git a/adev/src/app/core/services/version-manager.service.ts b/adev/src/app/core/services/version-manager.service.ts index fede9f915d65..bf0af31c225d 100644 --- a/adev/src/app/core/services/version-manager.service.ts +++ b/adev/src/app/core/services/version-manager.service.ts @@ -95,7 +95,7 @@ export class VersionManager { readonly currentDocsVersion = computed(() => { // In devmode the version is 0, so we'll target next (which is first on the list) - if (VERSION.major === '0') { + if (VERSION.major === '0' || VERSION.patch.includes('next')) { return this.versions()[0]; } diff --git a/adev/src/app/editor/code-editor/code-editor.component.html b/adev/src/app/editor/code-editor/code-editor.component.html index 57beae713fb9..46168c723682 100644 --- a/adev/src/app/editor/code-editor/code-editor.component.html +++ b/adev/src/app/editor/code-editor/code-editor.component.html @@ -128,7 +128,16 @@
    diff --git a/adev/src/app/editor/code-editor/code-editor.component.ts b/adev/src/app/editor/code-editor/code-editor.component.ts index 6d9ebef7c393..0c81ee66ef34 100644 --- a/adev/src/app/editor/code-editor/code-editor.component.ts +++ b/adev/src/app/editor/code-editor/code-editor.component.ts @@ -37,6 +37,8 @@ import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu'; import {FirebaseStudioLauncher} from '../firebase-studio-launcher.service'; import {MatTooltip} from '@angular/material/tooltip'; import {injectEmbeddedTutorialManager} from '../inject-embedded-tutorial-manager'; +import {NodeRuntimeState} from '../node-runtime-state.service'; +import {LoadingStep} from '../enums/loading-steps'; export const REQUIRED_FILES = new Set([ 'src/main.ts', @@ -65,6 +67,7 @@ export class CodeEditor { private readonly destroyRef = inject(DestroyRef); + private readonly nodeRuntimeState = inject(NodeRuntimeState); private readonly codeMirrorEditor = inject(CodeMirrorEditor); private readonly diagnosticsState = inject(DiagnosticsState); private readonly downloadManager = inject(DownloadManager); @@ -117,12 +120,68 @@ export class CodeEditor { this.listenToTabChange(); this.setSelectedTabOnTutorialChange(); + this.listenToFileOpenRequests(); }); cleanupFn(() => this.codeMirrorEditor.disable()); }); } + private listenToFileOpenRequests() { + // Handler for opening files at specific locations + const openFile = (file: string, line: number, character: number) => { + // Normalize the file path - Vite uses /app/... but editor uses src/app/... + let normalizedPath = file; + if (file.startsWith('/')) { + // Remove leading slash and prepend 'src' + normalizedPath = 'src' + file; + } + + // Find the file in the files list + const targetFile = this.files().find((f) => f.filename === normalizedPath); + if (targetFile) { + // Switch to the file's tab + const fileIndex = this.files().indexOf(targetFile); + this.matTabGroup().selectedIndex = fileIndex; + + // Explicitly change the current file in the editor + this.codeMirrorEditor.changeCurrentFile(targetFile.filename); + + // Wait for the tab to switch and file to load, then scroll to the line + setTimeout(() => { + this.codeMirrorEditor.scrollToLine(line - 1, character); // Convert to 0-based + }, 200); + } else { + // console.warn('File not found in editor:', normalizedPath); + } + }; + + // Listen for CustomEvent (backward compatibility) + const handleCustomEvent = (event: Event) => { + const customEvent = event as CustomEvent<{file: string; line: number; character: number}>; + const {file, line, character} = customEvent.detail; + openFile(file, line, character); + }; + + // Listen for postMessage from preview iframe (Vite error overlay) + const handlePostMessage = (event: MessageEvent) => { + // Check if this is an openFileAtLocation message + if (event.data?.type === 'openFileAtLocation') { + const {file, line, character} = event.data; + openFile(file, line, character); + } + }; + + window.addEventListener('openFileAtLocation', handleCustomEvent); + window.addEventListener('message', handlePostMessage); + + // Cleanup listeners on destroy + this.destroyRef.onDestroy(() => { + window.removeEventListener('openFileAtLocation', handleCustomEvent); + window.removeEventListener('message', handlePostMessage); + }); + } + protected openCurrentSolutionInFirebaseStudio(): void { this.firebaseStudioLauncher.openCurrentSolutionInFirebaseStudio(); } @@ -147,6 +206,25 @@ export class CodeEditor { this.displayErrorsBox.set(false); } + protected openFileAtLocation(error: DiagnosticWithLocation): void { + // Scroll the editor to the error location + // The error is always in the current file since diagnostics are file-specific + const lineNumber = error.lineNumber; + const characterPosition = error.characterPosition; + + // Calculate the position in the document + // CodeMirror uses 0-based line numbers, but our error uses 1-based + const line = Math.max(0, lineNumber - 1); + + // Request the editor to scroll to this line + // We'll need to add a method to CodeMirrorEditor service to handle this + this.codeMirrorEditor.scrollToLine(line, characterPosition); + } + + protected closeRenameFile(): void { + this.isRenamingFile.set(false); + } + protected canRenameFile = (filename: string) => this.canDeleteFile(filename); protected canDeleteFile(filename: string) { @@ -232,6 +310,9 @@ export class CodeEditor { private listenToDiagnosticsChange(): void { this.errors$.subscribe((diagnostics) => { + if (this.nodeRuntimeState.loadingStep() !== LoadingStep.READY) { + return; + } this.errors.set(diagnostics); this.displayErrorsBox.set(diagnostics.length > 0); }); diff --git a/adev/src/app/editor/code-editor/code-mirror-editor.service.ts b/adev/src/app/editor/code-editor/code-mirror-editor.service.ts index 9b4edb663237..4931f4616083 100644 --- a/adev/src/app/editor/code-editor/code-mirror-editor.service.ts +++ b/adev/src/app/editor/code-editor/code-mirror-editor.service.ts @@ -183,6 +183,28 @@ export class CodeMirrorEditor { this._editorView.setState(editorState); } + scrollToLine(line: number, character: number = 0): void { + if (!this._editorView) return; + + const state = this._editorView.state; + const doc = state.doc; + + if (line < 0 || line >= doc.lines) { + console.warn(`Line ${line} is out of bounds (0-${doc.lines - 1})`); + return; + } + + const lineObj = doc.line(line + 1); + const pos = lineObj.from + Math.min(character, lineObj.length); + + this._editorView.dispatch({ + selection: {anchor: pos, head: pos}, + scrollIntoView: true, + }); + + this._editorView.focus(); + } + private initTypescriptVfsWorker(): void { if (this.tsVfsWorker || !this.tsVfsWorkerFactory) { return; diff --git a/adev/src/app/editor/code-editor/constants/code-editor-extensions.ts b/adev/src/app/editor/code-editor/constants/code-editor-extensions.ts index c6c42bd62d85..6b0bf0b96c59 100644 --- a/adev/src/app/editor/code-editor/constants/code-editor-extensions.ts +++ b/adev/src/app/editor/code-editor/constants/code-editor-extensions.ts @@ -85,5 +85,9 @@ export const CODE_EDITOR_EXTENSIONS: Extension[] = [ run: startCompletion, mac: 'Mod-.', }, + { + key: 'Mod-s', + run: () => true, // Prevent default browser save dialog + }, ]), ]; diff --git a/adev/src/app/editor/error-filename-handler.ts b/adev/src/app/editor/error-filename-handler.ts new file mode 100644 index 000000000000..a82c0b71466f --- /dev/null +++ b/adev/src/app/editor/error-filename-handler.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {WebContainer} from '@webcontainer/api'; + +function errorFilenameHandler() { + const originalFetch = window.fetch; + window.fetch = async (input, init) => { + const url = input.toString(); + if (url.includes('__open-in-editor')) { + const params = new URLSearchParams(url.split('?')[1]); + const file = params.get('file'); + if (file) { + const [filepath, line, column] = file.split(':'); + window.parent.postMessage( + { + type: 'openFileAtLocation', + file: filepath, + line: parseInt(line, 10), + character: parseInt(column, 10), + }, + '*', + ); + } + return new Response(null, {status: 200}); + } + return originalFetch(input, init); + }; +} + +export async function setupErrorFilenameHandler(webContainer: WebContainer): Promise { + await webContainer.setPreviewScript(`(${errorFilenameHandler.toString()})()`); +} diff --git a/adev/src/app/editor/node-runtime-sandbox.service.ts b/adev/src/app/editor/node-runtime-sandbox.service.ts index c50c82339a08..35b7205b962b 100644 --- a/adev/src/app/editor/node-runtime-sandbox.service.ts +++ b/adev/src/app/editor/node-runtime-sandbox.service.ts @@ -8,6 +8,7 @@ import {DestroyRef, effect, inject, Injectable, signal} from '@angular/core'; import {FileSystemTree, WebContainer, WebContainerProcess} from '@webcontainer/api'; +import {setupErrorFilenameHandler} from './error-filename-handler'; import {BehaviorSubject, filter, map, Subject} from 'rxjs'; import {type FileAndContent, TutorialType, checkFilesInDirectory} from '@angular/docs'; @@ -398,6 +399,8 @@ export class NodeRuntimeSandbox { this.setErrorState(message, ErrorType.UNKNOWN); }); + + await setupErrorFilenameHandler(webContainer); } private checkForOutOfMemoryError(message: string): boolean { diff --git a/adev/src/app/features/docs/docs.component.html b/adev/src/app/features/docs/docs.component.html index a820c2214878..245163d788fb 100644 --- a/adev/src/app/features/docs/docs.component.html +++ b/adev/src/app/features/docs/docs.component.html @@ -1,8 +1,10 @@ @let docContent = this.docContent(); @if (docContent) { -
    + [hasToc]="true" + > } diff --git a/adev/src/app/features/home/animation/README.md b/adev/src/app/features/home/animation/README.md index af68e389fc5b..532609556cc9 100644 --- a/adev/src/app/features/home/animation/README.md +++ b/adev/src/app/features/home/animation/README.md @@ -34,11 +34,13 @@ class AnimationHost implements AfterViewInit { layers = viewChildren(AnimationLayerDirective); constructor() { - afterNextRender({ read: () => { - // The layers must be provided - const animation = this.animationCreator.createAnimation(this.layers()); - // ... - }}); + afterNextRender({ + read: () => { + // The layers must be provided + const animation = this.animationCreator.createAnimation(this.layers()); + // ... + }, + }); } } ``` @@ -73,8 +75,8 @@ const DEFINITION: AnimationDefinition = [ }, to: { 'background-color': '#fff', - } - } + }, + }, ]; ``` diff --git a/adev/src/app/features/home/animation/plugins/animation-player.component.ts b/adev/src/app/features/home/animation/plugins/animation-player.component.ts index 6cb65d9a03eb..4904252c3ded 100644 --- a/adev/src/app/features/home/animation/plugins/animation-player.component.ts +++ b/adev/src/app/features/home/animation/plugins/animation-player.component.ts @@ -49,7 +49,7 @@ export type ComponentAlignment = 'left' | 'center' | 'right'; bottom: 30px; padding: 10px; border-radius: 12px; - background: rgba(0,0,0, 0.7); + background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); z-index: 999999; diff --git a/adev/src/app/features/home/home.component.html b/adev/src/app/features/home/home.component.html index cdeb3ffa6c44..f13d163ce061 100644 --- a/adev/src/app/features/home/home.component.html +++ b/adev/src/app/features/home/home.component.html @@ -1,11 +1,7 @@
    - +

    Angular v21 is here!

    🕹️ Start your adventure

    diff --git a/adev/src/app/features/not-found/not-found.html b/adev/src/app/features/not-found/not-found.html new file mode 100644 index 000000000000..a4398293ef2f --- /dev/null +++ b/adev/src/app/features/not-found/not-found.html @@ -0,0 +1,81 @@ +
    +
    +
    +

    Page Not Found 🙃

    +
    +
    +

    + We couldn't find what you were looking for. We have initiated a search for the term extracted + from the URL. +

    +

    + If you think this is a mistake, please + + open an issue + so we can fix it. +

    + + + @if (searchResults().length > 0) { + + + } +
    diff --git a/adev/src/app/features/not-found/not-found.scss b/adev/src/app/features/not-found/not-found.scss new file mode 100644 index 000000000000..209dda0a5926 --- /dev/null +++ b/adev/src/app/features/not-found/not-found.scss @@ -0,0 +1,130 @@ +:host { + display: block; + padding-top: var(--layout-padding); + padding-bottom: var(--layout-padding); +} + +.docs-viewer { + display: flex; + flex-direction: column; + padding: 0px; + box-sizing: border-box; + padding-inline: var(--layout-padding); +} + +.enter-animation { + animation: slide-fade 1s; +} +@keyframes slide-fade { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.docs-search-results { + list-style-type: none; + padding-inline: 0; + padding-block-start: 1rem; + padding-block-end: 1rem; + margin: 0; + max-width: 750px; + + .docs-search-result { + border-inline-start: 2px solid var(--senary-contrast); + margin-inline-start: 1rem; + display: block; + font-size: 0.875rem; + + .docs-search-result-icon { + display: inline-block; + + i { + display: flex; + align-items: center; + font-size: 1.2rem; + } + } + + /** + * This rule needs ng-deep to be applied to elements that are created via a [innerHTML] binding + */ + ::ng-deep { + mark { + background: #e62600; + background: var(--red-to-orange-horizontal-gradient); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + } + } + + a { + color: var(--secondary-contrast); + display: flex; + justify-content: space-between; + padding: 1rem; + gap: 0.5rem; + } + + &__label, + &__sub-label, + &__content { + transition: color 0.3s ease; + } + + &__label, + &__sub-label { + display: flex; + align-items: center; + gap: 0.75rem; + margin: 0; + } + + &__label { + font-weight: 600; + flex-wrap: wrap; + + &__package { + font-size: 0.75rem; + } + } + + &__content, + &__category { + margin: 0; + } + + &__sub-label, + &__content { + margin-block-start: 0.375rem; + margin-inline-start: 2rem; + color: var(--quaternary-contrast); + } + + &__category { + font-weight: 400; + color: var(--quaternary-contrast); + } + + &.active { + background-color: var(--septenary-contrast); // stylelint-disable-line + } + + &:hover, + &.active { + background-color: var(--octonary-contrast); // stylelint-disable-line + border-inline-start: 2px solid var(--primary-contrast); + + .docs-search-result__label, + .docs-search-result__sub-label, + .docs-search-result__content { + color: var(--primary-contrast); + } + } + } +} diff --git a/adev/src/app/features/not-found/not-found.ts b/adev/src/app/features/not-found/not-found.ts new file mode 100644 index 000000000000..a1563e8df963 --- /dev/null +++ b/adev/src/app/features/not-found/not-found.ts @@ -0,0 +1,36 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component, inject, signal} from '@angular/core'; +import {ActivatedRoute, RouterLink, UrlSegment} from '@angular/router'; +import {Search, SearchItem, RelativeLink, SearchResultItem} from '@angular/docs'; + +@Component({ + imports: [SearchItem, RouterLink, RelativeLink], + templateUrl: './not-found.html', + styleUrl: './not-found.scss', +}) +export class NotFound { + private readonly search = inject(Search); + protected readonly searchResults = signal([]); + + constructor() { + const activatedRoute = inject(ActivatedRoute); + const searchTerms = this.extractSearchTerm(activatedRoute.snapshot.url); + if (searchTerms) { + // We're using the one-shot query request to not interfere with the main search signal + this.search.searchWithQuery(searchTerms).then((results) => { + this.searchResults.set(results ?? []); + }); + } + } + + private extractSearchTerm(url: UrlSegment[]): string { + return url.join(' ').replace(/[-_/]/g, ' '); + } +} diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.html b/adev/src/app/features/references/api-items-section/api-items-section.component.html index 8df416950bc6..32d7f9386d8d 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.html +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.html @@ -7,7 +7,7 @@

    class="adev-api-anchor" tabindex="-1" > - {{group().title}} + {{ group().title }}

    @@ -31,10 +31,10 @@

    @if (apiItem.deprecated) { <!> } - @if(apiItem.experimental) { + @if (apiItem.experimental) { 🧪 } - @if(apiItem.developerPreview) { + @if (apiItem.developerPreview) { 🚧 } diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.scss b/adev/src/app/features/references/api-items-section/api-items-section.component.scss index ee36fae6ccfe..ffed34e76aa8 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.scss +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.scss @@ -1,5 +1,3 @@ -@use '@angular/docs/styles/anchor' as anchor; - .adev-api-items-section-header { display: flex; @@ -92,6 +90,4 @@ .adev-api-anchor { color: inherit; - - @include anchor.docs-anchor(); } diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html index 60afa622e9a1..b2ed207e3be1 100644 --- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html +++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html @@ -1,5 +1,6 @@ @if (docContent(); as docContent) { -
    API Reference

    - @for (stat of statuses | keyvalue:null; track $index) { + @for (stat of statuses | keyvalue: null; track $index) { - {{statusLabels[stat.value]}} + {{ statusLabels[stat.value] }} } @@ -27,7 +27,7 @@

    API Reference

    (click)="setItemType(itemType)" > - {{ itemType | adevApiLabel : 'full' }} + {{ itemType | adevApiLabel: 'full' }} } diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.spec.ts b/adev/src/app/features/references/api-reference-list/api-reference-list.component.spec.ts index 62230adc0832..1083429b8dff 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.spec.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.spec.ts @@ -16,7 +16,7 @@ import {RouterTestingHarness} from '@angular/router/testing'; import {provideRouter} from '@angular/router'; import {Location} from '@angular/common'; import {By} from '@angular/platform-browser'; -import {NavigationItem, TextField} from '@angular/docs'; +import {TextField} from '@angular/docs'; import {ApiItem} from '../interfaces/api-item'; describe('ApiReferenceList', () => { @@ -117,6 +117,15 @@ describe('ApiReferenceList', () => { expect(component.filteredGroups()![0].items).toEqual([fakeItem1]); }); + it('should display items which match the query by group title', () => { + fixture.componentRef.setInput('query', 'Fake Group'); + fixture.detectChanges(); + + // Should find all items whose group title contains the query + expect(component.filteredGroups()![0].items.length).toBeGreaterThan(0); + expect(component.filteredGroups()![0].items).toContain(fakeItem1); + }); + it('should display only class items when user selects Class in the Type select', () => { fixture.componentRef.setInput('type', ApiItemType.CLASS); fixture.detectChanges(); diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts index 5977d7c718e1..a997b6c1ebbb 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts @@ -97,7 +97,10 @@ export default class ApiReferenceList { id: group.id, items: group.items.filter((apiItem) => { return ( - (query == '' ? true : apiItem.title.toLocaleLowerCase().includes(query)) && + (query == '' + ? true + : apiItem.title.toLocaleLowerCase().includes(query) || + group.title.toLocaleLowerCase().includes(query)) && (type === ALL_TYPES_KEY || apiItem.itemType === type) && ((status & STATUSES.stable && !apiItem.developerPreview && diff --git a/adev/src/app/features/references/interfaces/api-manifest.ts b/adev/src/app/features/references/interfaces/api-manifest.ts index e422ea3cf8d2..aec3d834f956 100644 --- a/adev/src/app/features/references/interfaces/api-manifest.ts +++ b/adev/src/app/features/references/interfaces/api-manifest.ts @@ -10,6 +10,7 @@ import {ApiItemType} from './api-item-type'; export interface ApiManifestEntry { name: string; + aliases?: string[]; type: ApiItemType; category: string | undefined; deprecated: {version: string | undefined} | undefined; diff --git a/adev/src/app/features/tutorial/tutorial-navigation-list.ts b/adev/src/app/features/tutorial/tutorial-navigation-list.ts index 68243567cca2..7ba2c882c4ae 100644 --- a/adev/src/app/features/tutorial/tutorial-navigation-list.ts +++ b/adev/src/app/features/tutorial/tutorial-navigation-list.ts @@ -15,29 +15,32 @@ import {NavigationItem} from '@angular/docs'; selector: 'adev-tutorial-navigation-list', imports: [RouterLink, RouterLinkActive, NgTemplateOutlet], template: ` - -
      - @for (item of navigationItems; track $index) { -
    • - - {{item.label}} - + +
        + @for (item of navigationItems; track $index) { +
      • + + {{ item.label }} + - @if (item.children && item.children.length > 0) { - + @if (item.children && item.children.length > 0) { + + } +
      • } - - } -
      -
      +
    +
    - + `, styleUrls: ['./tutorial-navigation-list.scss'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/src/app/features/tutorial/tutorial.component.html b/adev/src/app/features/tutorial/tutorial.component.html index d786c8fc23dd..1da8a56683f8 100644 --- a/adev/src/app/features/tutorial/tutorial.component.html +++ b/adev/src/app/features/tutorial/tutorial.component.html @@ -30,7 +30,7 @@
    @if (embeddedEditorComponent) { }
    diff --git a/adev/src/app/features/update/recommendations.ts b/adev/src/app/features/update/recommendations.ts index 60456e8abee0..a198a77cf8ec 100644 --- a/adev/src/app/features/update/recommendations.ts +++ b/adev/src/app/features/update/recommendations.ts @@ -2604,7 +2604,8 @@ export const RECOMMENDATIONS: Step[] = [ necessaryAsOf: 2000, level: ApplicationComplexity.Medium, step: '20.0.0_rename_rxResource_loader_to_stream', - action: 'Rename the `loader` property passed in rxResources to `stream`.', + action: + 'Rename the `request` and `loader` properties passed in RxResource to `params` and `stream`.', }, { possibleIn: 2000, diff --git a/adev/src/app/features/update/update.component.html b/adev/src/app/features/update/update.component.html index 776ae55de6ee..5ebb3dcbb8c9 100644 --- a/adev/src/app/features/update/update.component.html +++ b/adev/src/app/features/update/update.component.html @@ -19,7 +19,7 @@

    Angular versions

      @for (version of versions; track $index) {
    • -
    • @@ -40,7 +40,7 @@

      Angular versions

        @for (version of versions; track $index) {
      • -
      • @@ -70,7 +70,7 @@

        Angular versions

    } - @if ((to.number - from.number > 150) && from.number > 240) { + @if (to.number - from.number > 150 && from.number > 240) {

    Warning: @@ -86,7 +86,7 @@

    Application complexity

    Basic Medium @@ -106,7 +106,7 @@

    Other dependencies

    I use {{option.name}} {{option.description}}I use {{ option.name }} {{ option.description }}
    } @@ -137,16 +137,23 @@

    Package Manager

    @if ( - beforeRecommendations.length > 0 || duringRecommendations.length > 0 || afterRecommendations.length > 0 + beforeRecommendations.length > 0 || + duringRecommendations.length > 0 || + afterRecommendations.length > 0 ) {
    -

    {{title()}}

    +

    {{ title() }}

    Before you update

    - @for (r of beforeRecommendations; track $index) { + @for (r of beforeRecommendations; track $index) {
    -
    +
    +
    + + {{ getComplexityLevelName(r.level) }} + +
    } @if (beforeRecommendations.length <= 0) { @@ -165,7 +172,12 @@

    Update to the new version

    @for (r of duringRecommendations; track $index) {
    -
    +
    +
    + + {{ getComplexityLevelName(r.level) }} + +
    } @if (duringRecommendations.length <= 0) { @@ -178,7 +190,12 @@

    After you update

    @for (r of afterRecommendations; track $index) {
    -
    +
    +
    + + {{ getComplexityLevelName(r.level) }} + +
    } @if (afterRecommendations.length <= 0) { diff --git a/adev/src/app/features/update/update.component.scss b/adev/src/app/features/update/update.component.scss index e76b1e55562e..1cc5aa917bad 100644 --- a/adev/src/app/features/update/update.component.scss +++ b/adev/src/app/features/update/update.component.scss @@ -131,6 +131,42 @@ h4 { margin-top: 0.5rem; } + .adev-recommendation-content { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + } + + .adev-complexity-badge { + display: inline-block; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 500; + text-transform: uppercase; + white-space: nowrap; + flex-shrink: 0; + + --badge-color: var(--primary-text, black); + + color: var(--badge-color); + background: color-mix(in srgb, var(--badge-color) 10%, var(--page-background)); + + .docs-dark-mode & { + background: color-mix(in srgb, var(--badge-color) 17%, var(--page-background)); + } + + @include mq.for-tablet-landscape-down { + display: none; + } + } + + .adev-complexity-1 { --badge-color: var(--super-green); } + .adev-complexity-2 { --badge-color: var(--bright-blue); } + .adev-complexity-3 { --badge-color: var(--symbolic-orange); } + // Code blocks are generable from the markdown, we need to opt-out of the scoping ::ng-deep code { cursor: pointer; diff --git a/adev/src/app/features/update/update.component.spec.ts b/adev/src/app/features/update/update.component.spec.ts new file mode 100644 index 000000000000..b83d0f0d95c0 --- /dev/null +++ b/adev/src/app/features/update/update.component.spec.ts @@ -0,0 +1,96 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {provideRouter} from '@angular/router'; +import {provideHttpClient} from '@angular/common/http'; +import {provideHttpClientTesting} from '@angular/common/http/testing'; +import {By} from '@angular/platform-browser'; + +import UpdateComponent from './update.component'; +import {ApplicationComplexity} from './recommendations'; + +describe('UpdateComponent', () => { + let component: UpdateComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [UpdateComponent], + providers: [provideRouter([]), provideHttpClient(), provideHttpClientTesting()], + }); + fixture = TestBed.createComponent(UpdateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('getComplexityLevelName', () => { + it('should return "Basic" for ApplicationComplexity.Basic', () => { + expect(component['getComplexityLevelName'](ApplicationComplexity.Basic)).toBe('Basic'); + }); + + it('should return "Medium" for ApplicationComplexity.Medium', () => { + expect(component['getComplexityLevelName'](ApplicationComplexity.Medium)).toBe('Medium'); + }); + + it('should return "Advanced" for ApplicationComplexity.Advanced', () => { + expect(component['getComplexityLevelName'](ApplicationComplexity.Advanced)).toBe('Advanced'); + }); + + it('should return "Unknown" for invalid complexity level', () => { + expect(component['getComplexityLevelName'](999 as ApplicationComplexity)).toBe('Unknown'); + }); + }); + + describe('complexity badges rendering', () => { + it('should display complexity badges for recommendations', async () => { + // Find the "Show me how to update!" button and click it to trigger recommendations + const showButton: HTMLElement = fixture.debugElement.query( + By.css('.show-button'), + )?.nativeElement; + + expect(showButton).toBeTruthy(); + showButton.click(); + fixture.detectChanges(); + + // Wait for all async operations to complete (marked parsing, router navigation, etc.) + await fixture.whenStable(); + + // Additional wait for marked parsing + await new Promise((resolve) => setTimeout(resolve, 300)); + fixture.detectChanges(); + + const badges = fixture.nativeElement.querySelectorAll('.adev-complexity-badge'); + + // For the default versions (20.0 -> 21.0) with level 1, verify badges are rendered + if (badges.length > 0) { + // Check the first badge + const badge = badges[0]; + const badgeText = badge.textContent?.trim(); + + // Badge text should be one of the valid complexity levels + expect(['Basic', 'Medium', 'Advanced']).toContain(badgeText); + + // Badge should have appropriate CSS class + const hasComplexityClass = + badge.classList.contains('adev-complexity-1') || + badge.classList.contains('adev-complexity-2') || + badge.classList.contains('adev-complexity-3'); + expect(hasComplexityClass).toBe(true); + } else { + // If no recommendations exist for these versions, that's acceptable + // The test verifies the component renders without errors + expect(badges.length).toBe(0); + } + }); + }); +}); diff --git a/adev/src/app/features/update/update.component.ts b/adev/src/app/features/update/update.component.ts index 6ee0d2dee2fd..0b3539986f5b 100644 --- a/adev/src/app/features/update/update.component.ts +++ b/adev/src/app/features/update/update.component.ts @@ -7,7 +7,7 @@ */ import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core'; -import {Step, RECOMMENDATIONS} from './recommendations'; +import {Step, RECOMMENDATIONS, ApplicationComplexity} from './recommendations'; import {Clipboard} from '@angular/cdk/clipboard'; import {CdkMenuModule} from '@angular/cdk/menu'; import {MatCheckboxModule} from '@angular/material/checkbox'; @@ -294,6 +294,16 @@ export default class UpdateComponent { } } + protected getComplexityLevelName(level: ApplicationComplexity): string { + const names: Record = { + [ApplicationComplexity.Basic]: 'Basic', + [ApplicationComplexity.Medium]: 'Medium', + [ApplicationComplexity.Advanced]: 'Advanced', + }; + + return names[level] ?? 'Unknown'; + } + private replaceVariables(action: string): string { let newAction = action; newAction = newAction.replace( diff --git a/adev/src/app/routing/routes.ts b/adev/src/app/routing/routes.ts index 79758cbb555a..5d199e478991 100644 --- a/adev/src/app/routing/routes.ts +++ b/adev/src/app/routing/routes.ts @@ -45,7 +45,10 @@ const referencePageRoutes = mapNavigationItemsToRoutes( const updateGuidePageRoute: Route = { path: referenceNavigationItems.find((r) => r.path === DEFAULT_PAGES.UPDATE)!.path, loadComponent: () => import('../features/update/update.component'), - data: commonReferenceRouteData, + data: { + ...commonReferenceRouteData, + label: 'Update guide', + }, }; const cliReferencePageRoutes = mapNavigationItemsToRoutes( @@ -155,7 +158,7 @@ export const routes: Route[] = [ // Error page { path: '**', - loadComponent: () => import('../features/docs/docs.component'), - resolve: {'docContent': contentResolver('error')}, + loadComponent: () => import('../features/not-found/not-found').then((m) => m.NotFound), + data: {label: 'Page not found'}, }, ]; diff --git a/adev/src/app/routing/sub-navigation-data.ts b/adev/src/app/routing/sub-navigation-data.ts index a859ab2e1aa1..a5f0cdd05a95 100644 --- a/adev/src/app/routing/sub-navigation-data.ts +++ b/adev/src/app/routing/sub-navigation-data.ts @@ -1,4 +1,4 @@ -/*! +/** * @license * Copyright Google LLC All Rights Reserved. * @@ -9,7 +9,7 @@ import {isDevMode} from '@angular/core'; import {NavigationItem} from '@angular/docs'; -// These 2 imports are expected to be red because they are generated a build time +// These imports are expected to be red because they are generated a build time import FIRST_APP_TUTORIAL_NAV_DATA from '../../../src/assets/tutorials/first-app/routes.json'; import LEARN_ANGULAR_TUTORIAL_NAV_DATA from '../../../src/assets/tutorials/learn-angular/routes.json'; import DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA from '../../../src/assets/tutorials/deferrable-views/routes.json'; @@ -94,6 +94,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ children: [ { label: 'Signals', + status: 'updated', children: [ { label: 'Overview', @@ -110,6 +111,12 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/signals/resource', contentPath: 'guide/signals/resource', }, + { + label: 'Side effects for non-reactives APIs', + path: 'guide/signals/effect', + contentPath: 'guide/signals/effect', + status: 'new', + }, ], }, { @@ -443,6 +450,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/forms/signals/models', contentPath: 'guide/forms/signals/models', }, + { + label: 'Form model design', + path: 'guide/forms/signals/model-design', + contentPath: 'guide/forms/signals/designing-your-form-model', + }, { label: 'Field state management', path: 'guide/forms/signals/field-state-management', diff --git a/adev/src/assets/BUILD.bazel b/adev/src/assets/BUILD.bazel index 5835c283f5cd..ea9ca7f12955 100644 --- a/adev/src/assets/BUILD.bazel +++ b/adev/src/assets/BUILD.bazel @@ -12,7 +12,7 @@ copy_to_directory( "//adev/src/content/best-practices", "//adev/src/content/best-practices/runtime-performance", "//adev/src/content/cdk:cdk_docs", - "//adev/src/content/cli/help:cli_docs", + "//adev/src/content/cli:cli_docs", "//adev/src/content/ecosystem", "//adev/src/content/ecosystem/rxjs-interop", "//adev/src/content/ecosystem/service-workers", @@ -94,7 +94,7 @@ copy_to_directory( "//tools/manual_api_docs/elements:elements_docs", ], replace_prefixes = { - "adev/src/content/cli/help/cli_docs_html": "cli/", + "adev/src/content/cli/cli_docs_html": "cli/", "adev/src/content/aria/aria_docs_html": "api/", "adev/src/content/cdk/cdk_docs_html": "api/", "adev/src/content": "", diff --git a/adev/src/content/ai/design-patterns.md b/adev/src/content/ai/design-patterns.md index cdcd7530d318..d40582e24cdd 100644 --- a/adev/src/content/ai/design-patterns.md +++ b/adev/src/content/ai/design-patterns.md @@ -27,11 +27,14 @@ storyResource = resource({ loader: ({params}): Promise => { // The params value is the current value of the storyInput signal const url = this.endpoint(); - return runFlow({ url, input: { - userInput: params, - sessionId: this.storyService.sessionId() // Read from another signal - }}); - } + return runFlow({ + url, + input: { + userInput: params, + sessionId: this.storyService.sessionId(), // Read from another signal + }, + }); + }, }); ``` @@ -56,7 +59,7 @@ storyParts = linkedSignal({ const existingStoryParts = previous?.value || []; // Return a new array with the old and new parts return [...existingStoryParts, ...newStoryParts]; - } + }, }); ``` @@ -101,23 +104,23 @@ characters = resource({ // exposed by the Genkit client SDK const response = streamFlow({ url: '/streamCharacters', - input: 10 + input: 10, }); (async () => { for await (const chunk of response.stream) { data.update((prev) => { if ('value' in prev) { - return { value: `${prev.value} ${chunk}` }; + return {value: `${prev.value} ${chunk}`}; } else { - return { error: chunk as unknown as Error }; + return {error: chunk as unknown as Error}; } }); } })(); return data; - } + }, }); ``` @@ -136,38 +139,39 @@ The `characters` member is updated asynchronously and can be displayed in the te On the server side, in `server.ts` for example, the defined endpoint sends the data to be streamed to the client. The following code uses Gemini with the Genkit framework but this technique is applicable to other APIs that support streaming responses from LLMs: ```ts -import { startFlowServer } from '@genkit-ai/express'; -import { genkit } from "genkit/beta"; -import { googleAI, gemini20Flash } from "@genkit-ai/googleai"; +import {startFlowServer} from '@genkit-ai/express'; +import {genkit} from 'genkit/beta'; +import {googleAI, gemini20Flash} from '@genkit-ai/googleai'; -const ai = genkit({ plugins: [googleAI()] }); +const ai = genkit({plugins: [googleAI()]}); -export const streamCharacters = ai.defineFlow({ +export const streamCharacters = ai.defineFlow( + { name: 'streamCharacters', inputSchema: z.number(), outputSchema: z.string(), streamSchema: z.string(), }, - async (count, { sendChunk }) => { - const { response, stream } = ai.generateStream({ - model: gemini20Flash, - config: { - temperature: 1, - }, - prompt: `Generate ${count} different RPG game characters.`, - }); - - (async () => { - for await (const chunk of stream) { - sendChunk(chunk.content[0].text!); - } - })(); - - return (await response).text; -}); + async (count, {sendChunk}) => { + const {response, stream} = ai.generateStream({ + model: gemini20Flash, + config: { + temperature: 1, + }, + prompt: `Generate ${count} different RPG game characters.`, + }); + + (async () => { + for await (const chunk of stream) { + sendChunk(chunk.content[0].text!); + } + })(); + + return (await response).text; + }, +); startFlowServer({ flows: [streamCharacters], }); - ``` diff --git a/adev/src/content/ai/mcp-server-setup.md b/adev/src/content/ai/mcp-server-setup.md index 148c4311059d..7379067dfc78 100644 --- a/adev/src/content/ai/mcp-server-setup.md +++ b/adev/src/content/ai/mcp-server-setup.md @@ -19,9 +19,13 @@ The Angular CLI MCP server provides several tools to assist you in your developm Some tools are provided in experimental / preview status since they are new or not fully tested. Enable them individually with the [`--experimental-tool`](#command-options) option and use them with caution. -| Name | Description | `local-only` | `read-only` | -| :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------: | :---------: | -| `modernize` | Performs code migrations and provides further instructions on how to modernize Angular code to align with the latest best practices and syntax. [Learn more](https://angular.dev/reference/migrations) | ✅ | ❌ | +| Name | Description | `local-only` | `read-only` | +| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------: | :---------: | +| `build` | Perform a one-off, non-watched build using `ng build`. | ✅ | ❌ | +| `modernize` | Performs code migrations and provides further instructions on how to modernize Angular code to align with the latest best practices and syntax. [Learn more](/reference/migrations) | ✅ | ❌ | +| `start_devserver` | Asynchronously starts a development server that watches the workspace for changes, similar to running `ng serve`. Since this is asynchronous it returns immediately. To manage the resulting server, use the `stop_devserver` and `wait_for_devserver_build` tools. | ✅ | ✅ | +| `stop_devserver` | Stops a development server started by `start_devserver`. | ✅ | ✅ | +| `wait_for_devserver_build` | Returns the output logs of the most recent build in a running development server started by `start_devserver`. If a build is currently ongoing, it will first wait for that build to complete and then return the logs. | ✅ | ✅ | ## Get Started diff --git a/adev/src/content/aria/_build-info.json b/adev/src/content/aria/_build-info.json index 1f748a425ad8..1ba210c257ac 100644 --- a/adev/src/content/aria/_build-info.json +++ b/adev/src/content/aria/_build-info.json @@ -1,4 +1,4 @@ { "branchName": "refs/heads/main", - "sha": "476f7b12a420eab0ce5d9c0844478019fa24d0e2" + "sha": "d03c36f1e107ac2a9db0b8b9cd962ab4630adc89" } \ No newline at end of file diff --git a/adev/src/content/aria/aria-accordion.json b/adev/src/content/aria/aria-accordion.json index c20aa8aecf84..a020eb71ac95 100755 --- a/adev/src/content/aria/aria-accordion.json +++ b/adev/src/content/aria/aria-accordion.json @@ -4,6 +4,31 @@ "moduleName": "@angular/aria/accordion", "normalizedModuleName": "angular_aria_accordion", "entries": [ + { + "name": "AccordionContent", + "isAbstract": false, + "entryType": "undecorated_class", + "members": [], + "generics": [], + "description": "A structural directive that provides a mechanism for lazily rendering the content for an\n`ngAccordionPanel`.\n\nThis directive should be applied to an `ng-template` inside an `ngAccordionPanel`.\nIt allows the content of the panel to be lazily rendered, improving performance\nby only creating the content when the panel is first expanded.\n\n```html\n
    \n \n

    This is the content that will be displayed inside the panel.

    \n
    \n
    \n```", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" + }, + { + "name": "see", + "comment": "[Accordion](guide/aria/accordion)" + } + ], + "rawComment": "/**\n * A structural directive that provides a mechanism for lazily rendering the content for an\n * `ngAccordionPanel`.\n *\n * This directive should be applied to an `ng-template` inside an `ngAccordionPanel`.\n * It allows the content of the panel to be lazily rendered, improving performance\n * by only creating the content when the panel is first expanded.\n *\n * ```html\n *
    \n * \n *

    This is the content that will be displayed inside the panel.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */", + "implements": [], + "source": { + "filePath": "src/aria/accordion/accordion-content.ts", + "startLine": 31, + "endLine": 35 + } + }, { "name": "AccordionPanel", "isAbstract": false, @@ -11,7 +36,7 @@ "members": [ { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly" @@ -21,7 +46,7 @@ }, { "name": "panelId", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly" @@ -31,7 +56,7 @@ }, { "name": "visible", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -145,14 +170,18 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Accordion](guide/aria/accordion)" } ], - "rawComment": "/**\n * The content panel of an accordion item that is conditionally visible.\n *\n * This directive is a container for the content that is shown or hidden. It requires\n * a `panelId` that must match the `panelId` of its corresponding `ngAccordionTrigger`.\n * The content within the panel should be provided using an `ng-template` with the\n * `ngAccordionContent` directive so that the content is not rendered on the page until the trigger\n * is expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\n * its content from assistive technologies when not visible.\n *\n * ```html\n *
    \n * \n *

    This content is lazily rendered and will be shown when the panel is expanded.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * The content panel of an accordion item that is conditionally visible.\n *\n * This directive is a container for the content that is shown or hidden. It requires\n * a `panelId` that must match the `panelId` of its corresponding `ngAccordionTrigger`.\n * The content within the panel should be provided using an `ng-template` with the\n * `ngAccordionContent` directive so that the content is not rendered on the page until the trigger\n * is expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\n * its content from assistive technologies when not visible.\n *\n * ```html\n *
    \n * \n *

    This content is lazily rendered and will be shown when the panel is expanded.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */", "implements": [], "source": { - "filePath": "src/aria/accordion/accordion.ts", - "startLine": 52, - "endLine": 113 + "filePath": "/src/aria/accordion/accordion-panel.ts", + "startLine": 42, + "endLine": 103 } }, { @@ -172,7 +201,7 @@ }, { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -185,7 +214,7 @@ }, { "name": "panelId", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -198,7 +227,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -211,7 +240,7 @@ }, { "name": "expanded", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "readonly", @@ -226,7 +255,7 @@ }, { "name": "active", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -340,9 +369,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Accordion](guide/aria/accordion)" } ], - "rawComment": "/**\n * The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n *\n * This directive requires a `panelId` that must match the `panelId` of the `ngAccordionPanel` it\n * controls. When clicked, it will expand or collapse the panel. It also handles keyboard\n * interactions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n * `aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\n * The `disabled` input can be used to disable the trigger.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n *\n * This directive requires a `panelId` that must match the `panelId` of the `ngAccordionPanel` it\n * controls. When clicked, it will expand or collapse the panel. It also handles keyboard\n * interactions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n * `aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\n * The `disabled` input can be used to disable the trigger.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */", "implements": [], "isStandalone": true, "selector": "[ngAccordionTrigger]", @@ -350,9 +383,9 @@ "ngAccordionTrigger" ], "source": { - "filePath": "src/aria/accordion/accordion.ts", - "startLine": 132, - "endLine": 197 + "filePath": "/src/aria/accordion/accordion-trigger.ts", + "startLine": 42, + "endLine": 107 } }, { @@ -372,7 +405,7 @@ }, { "name": "textDirection", - "type": "any", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" @@ -382,7 +415,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -395,7 +428,7 @@ }, { "name": "multiExpandable", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -408,7 +441,7 @@ }, { "name": "softDisabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -421,7 +454,7 @@ }, { "name": "wrap", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -505,9 +538,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Accordion](guide/aria/accordion)" } ], - "rawComment": "/**\n * A container for a group of accordion items. It manages the overall state and\n * interactions of the accordion, such as keyboard navigation and expansion mode.\n *\n * The `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\n * coordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\n * It supports both single and multiple expansion modes.\n *\n * ```html\n *
    \n *
    \n *

    \n * \n *

    \n *
    \n * \n *

    Content for Item 1.

    \n *
    \n *
    \n *
    \n *
    \n *

    \n * \n *

    \n *
    \n * \n *

    Content for Item 2.

    \n *
    \n *
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A container for a group of accordion items. It manages the overall state and\n * interactions of the accordion, such as keyboard navigation and expansion mode.\n *\n * The `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\n * coordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\n * It supports both single and multiple expansion modes.\n *\n * ```html\n *
    \n *
    \n *

    \n * \n *

    \n *
    \n * \n *

    Content for Item 1.

    \n *
    \n *
    \n *
    \n *
    \n *

    \n * \n *

    \n *
    \n * \n *

    Content for Item 2.

    \n *
    \n *
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */", "implements": [], "isStandalone": true, "selector": "[ngAccordionGroup]", @@ -515,30 +552,9 @@ "ngAccordionGroup" ], "source": { - "filePath": "src/aria/accordion/accordion.ts", - "startLine": 234, - "endLine": 329 - } - }, - { - "name": "AccordionContent", - "isAbstract": false, - "entryType": "undecorated_class", - "members": [], - "generics": [], - "description": "A structural directive that provides a mechanism for lazily rendering the content for an\n`ngAccordionPanel`.\n\nThis directive should be applied to an `ng-template` inside an `ngAccordionPanel`.\nIt allows the content of the panel to be lazily rendered, improving performance\nby only creating the content when the panel is first expanded.\n\n```html\n
    \n \n

    This is the content that will be displayed inside the panel.

    \n
    \n
    \n```", - "jsdocTags": [ - { - "name": "developerPreview", - "comment": "21.0" - } - ], - "rawComment": "/**\n * A structural directive that provides a mechanism for lazily rendering the content for an\n * `ngAccordionPanel`.\n *\n * This directive should be applied to an `ng-template` inside an `ngAccordionPanel`.\n * It allows the content of the panel to be lazily rendered, improving performance\n * by only creating the content when the panel is first expanded.\n *\n * ```html\n *
    \n * \n *

    This is the content that will be displayed inside the panel.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], - "source": { - "filePath": "src/aria/accordion/accordion.ts", - "startLine": 349, - "endLine": 353 + "filePath": "src/aria/accordion/accordion-group.ts", + "startLine": 62, + "endLine": 158 } } ], @@ -551,18 +567,10 @@ "input", "@angular/core" ], - [ - "ElementRef", - "@angular/core" - ], [ "inject", "@angular/core" ], - [ - "contentChildren", - "@angular/core" - ], [ "afterRenderEffect", "@angular/core" @@ -571,14 +579,6 @@ "signal", "@angular/core" ], - [ - "model", - "@angular/core" - ], - [ - "booleanAttribute", - "@angular/core" - ], [ "computed", "@angular/core" @@ -592,28 +592,28 @@ "@angular/cdk/a11y" ], [ - "Directionality", - "@angular/cdk/bidi" + "ElementRef", + "@angular/core" ], [ - "DeferredContent", - "@angular/aria/private" + "model", + "@angular/core" ], [ - "DeferredContentAware", - "@angular/aria/private" + "booleanAttribute", + "@angular/core" ], [ - "AccordionGroupPattern", - "@angular/aria/private" + "contentChildren", + "@angular/core" ], [ - "AccordionPanelPattern", - "@angular/aria/private" + "Directionality", + "@angular/cdk/bidi" ], [ - "AccordionTriggerPattern", - "@angular/aria/private" + "AccordionContent", + "@angular/aria/accordion" ], [ "AccordionPanel", @@ -734,10 +734,6 @@ [ "AccordionGroup.collapseAll", "@angular/aria/accordion" - ], - [ - "AccordionContent", - "@angular/aria/accordion" ] ] } \ No newline at end of file diff --git a/adev/src/content/aria/aria-combobox.json b/adev/src/content/aria/aria-combobox.json index 1ac7e96b4978..8669dc0c0ae8 100755 --- a/adev/src/content/aria/aria-combobox.json +++ b/adev/src/content/aria/aria-combobox.json @@ -5,153 +5,79 @@ "normalizedModuleName": "angular_aria_combobox", "entries": [ { - "name": "Combobox", + "name": "ComboboxPopupContainer", "isAbstract": false, "entryType": "undecorated_class", - "members": [ - { - "name": "textDirection", - "type": "any", - "memberType": "property", - "memberTags": [ - "protected" - ], - "description": "A signal wrapper for directionality.", - "jsdocTags": [] - }, - { - "name": "element", - "type": "HTMLElement", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "A reference to the combobox element.", - "jsdocTags": [] - }, - { - "name": "popup", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The combobox popup.", - "jsdocTags": [] - }, + "members": [], + "generics": [], + "description": "A structural directive that marks the `ng-template` to be used as the popup\nfor a combobox. This content is conditionally rendered.\n\nThe content of the popup can be a `ngListbox`, `ngTree`, or `role=\"dialog\"`, allowing for\nflexible and complex combobox implementations. The consumer is responsible for\nimplementing the filtering logic based on the `ngComboboxInput`'s value.\n\n```html\n\n
    \n \n
    \n
    \n```\n\nWhen using CdkOverlay, this directive can be replaced by `cdkConnectedOverlay`.\n\n```html\n\n
    \n \n
    \n\n```", + "jsdocTags": [ { - "name": "filterMode", - "type": "any", - "memberType": "property", - "memberTags": [], - "description": "The filter mode for the combobox.\n- `manual`: The consumer is responsible for filtering the options.\n- `auto-select`: The combobox automatically selects the first matching option.\n- `highlight`: The combobox highlights matching text in the options without changing selection.", - "jsdocTags": [] + "name": "developerPreview", + "comment": "21.0" }, { - "name": "disabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the combobox is disabled.", - "jsdocTags": [] + "name": "see", + "comment": "[Combobox](guide/aria/combobox)" }, { - "name": "readonly", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the combobox is read-only.", - "jsdocTags": [] + "name": "see", + "comment": "[Select](guide/aria/select)" }, { - "name": "firstMatch", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The value of the first matching item in the popup.", - "jsdocTags": [] + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" }, { - "name": "expanded", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the combobox is expanded.", - "jsdocTags": [] - }, + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" + } + ], + "rawComment": "/**\n * A structural directive that marks the `ng-template` to be used as the popup\n * for a combobox. This content is conditionally rendered.\n *\n * The content of the popup can be a `ngListbox`, `ngTree`, or `role=\"dialog\"`, allowing for\n * flexible and complex combobox implementations. The consumer is responsible for\n * implementing the filtering logic based on the `ngComboboxInput`'s value.\n *\n * ```html\n * \n *
    \n * \n *
    \n *
    \n * ```\n *\n * When using CdkOverlay, this directive can be replaced by `cdkConnectedOverlay`.\n *\n * ```html\n * \n *
    \n * \n *
    \n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Combobox](guide/aria/combobox)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n * @see [Autocomplete](guide/aria/autocomplete)\n */", + "implements": [], + "source": { + "filePath": "src/aria/combobox/combobox-popup-container.ts", + "startLine": 47, + "endLine": 52 + } + }, + { + "name": "ComboboxDialog", + "isAbstract": false, + "entryType": "directive", + "members": [ { - "name": "alwaysExpanded", - "type": "any", + "name": "element", + "type": "HTMLElement", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the combobox popup should always be expanded, regardless of user interaction.", + "description": "A reference to the dialog element.", "jsdocTags": [] }, { - "name": "inputElement", - "type": "any", + "name": "combobox", + "type": "Combobox", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Input element connected to the combobox, if any.", + "description": "The combobox that the dialog belongs to.", "jsdocTags": [] }, - { - "name": "open", - "signatures": [ - { - "name": "open", - "entryType": "function", - "description": "Opens the combobox to the selected item.", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [], - "rawComment": "/** Opens the combobox to the selected item. */", - "returnType": "void" - } - ], - "implementation": { - "params": [], - "isNewType": false, - "returnType": "void", - "generics": [], - "name": "open", - "description": "Opens the combobox to the selected item.", - "entryType": "function", - "jsdocTags": [], - "rawComment": "/** Opens the combobox to the selected item. */" - }, - "entryType": "function", - "description": "Opens the combobox to the selected item.", - "jsdocTags": [], - "rawComment": "/** Opens the combobox to the selected item. */", - "memberType": "method", - "memberTags": [] - }, { "name": "close", "signatures": [ { "name": "close", "entryType": "function", - "description": "Closes the combobox.", + "description": "", "generics": [], "isNewType": false, "jsdocTags": [], "params": [], - "rawComment": "/** Closes the combobox. */", + "rawComment": "", "returnType": "void" } ], @@ -161,41 +87,54 @@ "returnType": "void", "generics": [], "name": "close", - "description": "Closes the combobox.", + "description": "", "entryType": "function", "jsdocTags": [], - "rawComment": "/** Closes the combobox. */" + "rawComment": "" }, "entryType": "function", - "description": "Closes the combobox.", + "description": "", "jsdocTags": [], - "rawComment": "/** Closes the combobox. */", + "rawComment": "", "memberType": "method", "memberTags": [] } ], - "generics": [ - { - "name": "V" - } - ], - "description": "The container element that wraps a combobox input and popup, and orchestrates its behavior.\n\nThe `ngCombobox` directive is the main entry point for creating a combobox and customizing its\nbehavior. It coordinates the interactions between the `ngComboboxInput` and the popup, which\nis defined by a `ng-template` with the `ngComboboxPopupContainer` directive. If using the\n`CdkOverlay`, the `cdkConnectedOverlay` directive takes the place of `ngComboboxPopupContainer`.\n\n```html\n
    \n \n\n \n
    ", + "generics": [], + "description": "Integrates a native `` element with the combobox, allowing for\na modal or non-modal popup experience. It handles the opening and closing of the dialog\nbased on the combobox's expanded state.\n\n```html\n\n \n \n \n\n```", "jsdocTags": [ - { - "name": "for", - "comment": "(option of filteredOptions(); track option) {\n
    \n{{option}}\n
    \n}\n
    \n
    \n
    \n```" - }, { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Combobox](guide/aria/combobox)" + }, + { + "name": "see", + "comment": "[Select](guide/aria/select)" + }, + { + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" + }, + { + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" } ], - "rawComment": "/**\n * The container element that wraps a combobox input and popup, and orchestrates its behavior.\n *\n * The `ngCombobox` directive is the main entry point for creating a combobox and customizing its\n * behavior. It coordinates the interactions between the `ngComboboxInput` and the popup, which\n * is defined by a `ng-template` with the `ngComboboxPopupContainer` directive. If using the\n * `CdkOverlay`, the `cdkConnectedOverlay` directive takes the place of `ngComboboxPopupContainer`.\n *\n * ```html\n *
    \n * \n *\n * \n *
    \n * @for (option of filteredOptions(); track option) {\n *
    \n * {{option}}\n *
    \n * }\n *
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * Integrates a native `` element with the combobox, allowing for\n * a modal or non-modal popup experience. It handles the opening and closing of the dialog\n * based on the combobox's expanded state.\n *\n * ```html\n * \n * \n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Combobox](guide/aria/combobox)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n * @see [Autocomplete](guide/aria/autocomplete)\n */", "implements": [], + "isStandalone": true, + "selector": "dialog[ngComboboxDialog]", + "exportAs": [ + "ngComboboxDialog" + ], "source": { - "filePath": "src/aria/combobox/combobox.ts", - "startLine": 64, - "endLine": 170 + "filePath": "src/aria/combobox/combobox-dialog.ts", + "startLine": 34, + "endLine": 84 } }, { @@ -215,7 +154,7 @@ }, { "name": "combobox", - "type": "any", + "type": "Combobox", "memberType": "property", "memberTags": [ "readonly" @@ -225,7 +164,7 @@ }, { "name": "value", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "input", @@ -244,9 +183,25 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Combobox](guide/aria/combobox)" + }, + { + "name": "see", + "comment": "[Select](guide/aria/select)" + }, + { + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" + }, + { + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" } ], - "rawComment": "/**\n * An input that is part of a combobox. It is responsible for displaying the\n * current value and handling user input for filtering and selection.\n *\n * This directive should be applied to an `` element within an `ngCombobox`\n * container. It automatically handles keyboard interactions, such as opening the\n * popup and navigating through the options.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * An input that is part of a combobox. It is responsible for displaying the\n * current value and handling user input for filtering and selection.\n *\n * This directive should be applied to an `` element within an `ngCombobox`\n * container. It automatically handles keyboard interactions, such as opening the\n * popup and navigating through the options.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Combobox](guide/aria/combobox)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n * @see [Autocomplete](guide/aria/autocomplete)\n */", "implements": [], "isStandalone": true, "selector": "input[ngComboboxInput]", @@ -254,30 +209,9 @@ "ngComboboxInput" ], "source": { - "filePath": "src/aria/combobox/combobox.ts", - "startLine": 190, - "endLine": 236 - } - }, - { - "name": "ComboboxPopupContainer", - "isAbstract": false, - "entryType": "undecorated_class", - "members": [], - "generics": [], - "description": "A structural directive that marks the `ng-template` to be used as the popup\nfor a combobox. This content is conditionally rendered.\n\nThe content of the popup can be a `ngListbox`, `ngTree`, or `role=\"dialog\"`, allowing for\nflexible and complex combobox implementations. The consumer is responsible for\nimplementing the filtering logic based on the `ngComboboxInput`'s value.\n\n```html\n\n
    \n \n
    \n
    \n```\n\nWhen using CdkOverlay, this directive can be replaced by `cdkConnectedOverlay`.\n\n```html\n\n
    \n \n
    \n\n```", - "jsdocTags": [ - { - "name": "developerPreview", - "comment": "21.0" - } - ], - "rawComment": "/**\n * A structural directive that marks the `ng-template` to be used as the popup\n * for a combobox. This content is conditionally rendered.\n *\n * The content of the popup can be a `ngListbox`, `ngTree`, or `role=\"dialog\"`, allowing for\n * flexible and complex combobox implementations. The consumer is responsible for\n * implementing the filtering logic based on the `ngComboboxInput`'s value.\n *\n * ```html\n * \n *
    \n * \n *
    \n *
    \n * ```\n *\n * When using CdkOverlay, this directive can be replaced by `cdkConnectedOverlay`.\n *\n * ```html\n * \n *
    \n * \n *
    \n * \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], - "source": { - "filePath": "src/aria/combobox/combobox.ts", - "startLine": 268, - "endLine": 273 + "filePath": "src/aria/combobox/combobox-input.ts", + "startLine": 44, + "endLine": 90 } }, { @@ -287,7 +221,7 @@ "members": [ { "name": "combobox", - "type": "any", + "type": "Combobox | null", "memberType": "property", "memberTags": [ "readonly" @@ -306,9 +240,25 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Combobox](guide/aria/combobox)" + }, + { + "name": "see", + "comment": "[Select](guide/aria/select)" + }, + { + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" + }, + { + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" } ], - "rawComment": "/**\n * Identifies an element as a popup for an `ngCombobox`.\n *\n * This directive acts as a bridge, allowing the `ngCombobox` to discover and interact\n * with the underlying control (e.g., `ngListbox`, `ngTree`, or `ngComboboxDialog`) that\n * manages the options. It's primarily used as a host directive and is responsible for\n * exposing the popup's control pattern to the parent combobox.\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * Identifies an element as a popup for an `ngCombobox`.\n *\n * This directive acts as a bridge, allowing the `ngCombobox` to discover and interact\n * with the underlying control (e.g., `ngListbox`, `ngTree`, or `ngComboboxDialog`) that\n * manages the options. It's primarily used as a host directive and is responsible for\n * exposing the popup's control pattern to the parent combobox.\n *\n * @developerPreview 21.0\n *\n * @see [Combobox](guide/aria/combobox)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n * @see [Autocomplete](guide/aria/autocomplete)\n */", "implements": [], "isStandalone": true, "selector": "[ngComboboxPopup]", @@ -316,16 +266,26 @@ "ngComboboxPopup" ], "source": { - "filePath": "src/aria/combobox/combobox.ts", - "startLine": 285, - "endLine": 300 + "filePath": "/src/aria/combobox/combobox-popup.ts", + "startLine": 29, + "endLine": 44 } }, { - "name": "ComboboxDialog", + "name": "Combobox", "isAbstract": false, - "entryType": "directive", + "entryType": "undecorated_class", "members": [ + { + "name": "textDirection", + "type": "Signal", + "memberType": "property", + "memberTags": [ + "protected" + ], + "description": "A signal wrapper for directionality.", + "jsdocTags": [] + }, { "name": "element", "type": "HTMLElement", @@ -333,31 +293,132 @@ "memberTags": [ "readonly" ], - "description": "A reference to the dialog element.", + "description": "A reference to the combobox element.", "jsdocTags": [] }, { - "name": "combobox", - "type": "any", + "name": "popup", + "type": "Signal | undefined>", "memberType": "property", "memberTags": [ "readonly" ], - "description": "The combobox that the dialog belongs to.", + "description": "The combobox popup.", + "jsdocTags": [] + }, + { + "name": "filterMode", + "type": "InputSignal<\"manual\" | \"auto-select\" | \"highlight\">", + "memberType": "property", + "memberTags": [], + "description": "The filter mode for the combobox.\n- `manual`: The consumer is responsible for filtering the options.\n- `auto-select`: The combobox automatically selects the first matching option.\n- `highlight`: The combobox highlights matching text in the options without changing selection.", + "jsdocTags": [] + }, + { + "name": "disabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the combobox is disabled.", + "jsdocTags": [] + }, + { + "name": "readonly", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the combobox is read-only.", + "jsdocTags": [] + }, + { + "name": "firstMatch", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The value of the first matching item in the popup.", + "jsdocTags": [] + }, + { + "name": "expanded", + "type": "Signal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the combobox is expanded.", + "jsdocTags": [] + }, + { + "name": "alwaysExpanded", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the combobox popup should always be expanded, regardless of user interaction.", + "jsdocTags": [] + }, + { + "name": "inputElement", + "type": "Signal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Input element connected to the combobox, if any.", "jsdocTags": [] }, + { + "name": "open", + "signatures": [ + { + "name": "open", + "entryType": "function", + "description": "Opens the combobox to the selected item.", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [], + "rawComment": "/** Opens the combobox to the selected item. */", + "returnType": "void" + } + ], + "implementation": { + "params": [], + "isNewType": false, + "returnType": "void", + "generics": [], + "name": "open", + "description": "Opens the combobox to the selected item.", + "entryType": "function", + "jsdocTags": [], + "rawComment": "/** Opens the combobox to the selected item. */" + }, + "entryType": "function", + "description": "Opens the combobox to the selected item.", + "jsdocTags": [], + "rawComment": "/** Opens the combobox to the selected item. */", + "memberType": "method", + "memberTags": [] + }, { "name": "close", "signatures": [ { "name": "close", "entryType": "function", - "description": "", + "description": "Closes the combobox.", "generics": [], "isNewType": false, "jsdocTags": [], "params": [], - "rawComment": "", + "rawComment": "/** Closes the combobox. */", "returnType": "void" } ], @@ -367,60 +428,63 @@ "returnType": "void", "generics": [], "name": "close", - "description": "", + "description": "Closes the combobox.", "entryType": "function", "jsdocTags": [], - "rawComment": "" + "rawComment": "/** Closes the combobox. */" }, "entryType": "function", - "description": "", + "description": "Closes the combobox.", "jsdocTags": [], - "rawComment": "", + "rawComment": "/** Closes the combobox. */", "memberType": "method", "memberTags": [] } ], - "generics": [], - "description": "Integrates a native `` element with the combobox, allowing for\na modal or non-modal popup experience. It handles the opening and closing of the dialog\nbased on the combobox's expanded state.\n\n```html\n\n \n \n \n\n```", + "generics": [ + { + "name": "V" + } + ], + "description": "The container element that wraps a combobox input and popup, and orchestrates its behavior.\n\nThe `ngCombobox` directive is the main entry point for creating a combobox and customizing its\nbehavior. It coordinates the interactions between the `ngComboboxInput` and the popup, which\nis defined by a `ng-template` with the `ngComboboxPopupContainer` directive. If using the\n`CdkOverlay`, the `cdkConnectedOverlay` directive takes the place of `ngComboboxPopupContainer`.\n\n```html\n
    \n \n\n \n
    \n @for (option of filteredOptions(); track option) {\n
    \n {{option}}\n
    \n }\n
    \n
    \n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Combobox](guide/aria/combobox)" + }, + { + "name": "see", + "comment": "[Select](guide/aria/select)" + }, + { + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" + }, + { + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" } ], - "rawComment": "/**\n * Integrates a native `` element with the combobox, allowing for\n * a modal or non-modal popup experience. It handles the opening and closing of the dialog\n * based on the combobox's expanded state.\n *\n * ```html\n * \n * \n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * The container element that wraps a combobox input and popup, and orchestrates its behavior.\n *\n * The `ngCombobox` directive is the main entry point for creating a combobox and customizing its\n * behavior. It coordinates the interactions between the `ngComboboxInput` and the popup, which\n * is defined by a `ng-template` with the `ngComboboxPopupContainer` directive. If using the\n * `CdkOverlay`, the `cdkConnectedOverlay` directive takes the place of `ngComboboxPopupContainer`.\n *\n * ```html\n *
    \n * \n *\n * \n *
    \n * @for (option of filteredOptions(); track option) {\n *
    \n * {{option}}\n *
    \n * }\n *
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Combobox](guide/aria/combobox)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n * @see [Autocomplete](guide/aria/autocomplete)\n */", "implements": [], - "isStandalone": true, - "selector": "dialog[ngComboboxDialog]", - "exportAs": [ - "ngComboboxDialog" - ], "source": { - "filePath": "src/aria/combobox/combobox.ts", - "startLine": 317, - "endLine": 367 + "filePath": "/src/aria/combobox/combobox.ts", + "startLine": 60, + "endLine": 162 } } ], "symbols": [ [ - "afterRenderEffect", - "@angular/core" - ], - [ - "booleanAttribute", - "@angular/core" - ], - [ - "computed", - "@angular/core" - ], - [ - "contentChild", + "Directive", "@angular/core" ], [ - "Directive", + "afterRenderEffect", "@angular/core" ], [ @@ -431,18 +495,10 @@ "inject", "@angular/core" ], - [ - "input", - "@angular/core" - ], [ "model", "@angular/core" ], - [ - "signal", - "@angular/core" - ], [ "untracked", "@angular/core" @@ -452,47 +508,39 @@ "@angular/core" ], [ - "DeferredContent", - "@angular/aria/private" - ], - [ - "DeferredContentAware", - "@angular/aria/private" + "signal", + "@angular/core" ], [ - "ComboboxPattern", - "@angular/aria/private" + "booleanAttribute", + "@angular/core" ], [ - "ComboboxListboxControls", - "@angular/aria/private" + "computed", + "@angular/core" ], [ - "ComboboxTreeControls", - "@angular/aria/private" + "contentChild", + "@angular/core" ], [ - "ComboboxDialogPattern", - "@angular/aria/private" + "input", + "@angular/core" ], [ "Directionality", "@angular/cdk/bidi" ], [ - "toSignal", - "@angular/core/rxjs-interop" - ], - [ - "Combobox", + "ComboboxPopupContainer", "@angular/aria/combobox" ], [ - "ComboboxInput", + "ComboboxDialog", "@angular/aria/combobox" ], [ - "ComboboxPopupContainer", + "ComboboxInput", "@angular/aria/combobox" ], [ @@ -500,103 +548,103 @@ "@angular/aria/combobox" ], [ - "ComboboxDialog", + "Combobox", "@angular/aria/combobox" ], [ - "Combobox", + "ComboboxPopupContainer", "@angular/aria/combobox" ], [ - "Combobox.textDirection", + "ComboboxDialog", "@angular/aria/combobox" ], [ - "Combobox.element", + "ComboboxDialog.element", "@angular/aria/combobox" ], [ - "Combobox.popup", + "ComboboxDialog.combobox", "@angular/aria/combobox" ], [ - "Combobox.filterMode", + "ComboboxDialog.close", "@angular/aria/combobox" ], [ - "Combobox.disabled", + "ComboboxInput", "@angular/aria/combobox" ], [ - "Combobox.readonly", + "ComboboxInput.element", "@angular/aria/combobox" ], [ - "Combobox.firstMatch", + "ComboboxInput.combobox", "@angular/aria/combobox" ], [ - "Combobox.expanded", + "ComboboxInput.value", "@angular/aria/combobox" ], [ - "Combobox.alwaysExpanded", + "ComboboxPopup", "@angular/aria/combobox" ], [ - "Combobox.inputElement", + "ComboboxPopup.combobox", "@angular/aria/combobox" ], [ - "Combobox.open", + "Combobox", "@angular/aria/combobox" ], [ - "Combobox.close", + "Combobox.textDirection", "@angular/aria/combobox" ], [ - "ComboboxInput", + "Combobox.element", "@angular/aria/combobox" ], [ - "ComboboxInput.element", + "Combobox.popup", "@angular/aria/combobox" ], [ - "ComboboxInput.combobox", + "Combobox.filterMode", "@angular/aria/combobox" ], [ - "ComboboxInput.value", + "Combobox.disabled", "@angular/aria/combobox" ], [ - "ComboboxPopupContainer", + "Combobox.readonly", "@angular/aria/combobox" ], [ - "ComboboxPopup", + "Combobox.firstMatch", "@angular/aria/combobox" ], [ - "ComboboxPopup.combobox", + "Combobox.expanded", "@angular/aria/combobox" ], [ - "ComboboxDialog", + "Combobox.alwaysExpanded", "@angular/aria/combobox" ], [ - "ComboboxDialog.element", + "Combobox.inputElement", "@angular/aria/combobox" ], [ - "ComboboxDialog.combobox", + "Combobox.open", "@angular/aria/combobox" ], [ - "ComboboxDialog.close", + "Combobox.close", "@angular/aria/combobox" ] ] diff --git a/adev/src/content/aria/aria-grid.json b/adev/src/content/aria/aria-grid.json index b25f38175aa0..0ff0bc177c81 100755 --- a/adev/src/content/aria/aria-grid.json +++ b/adev/src/content/aria/aria-grid.json @@ -5,7 +5,7 @@ "normalizedModuleName": "angular_aria_grid", "entries": [ { - "name": "Grid", + "name": "GridRow", "isAbstract": false, "entryType": "directive", "members": [ @@ -20,214 +20,260 @@ "jsdocTags": [] }, { - "name": "textDirection", - "type": "any", + "name": "rowIndex", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "Text direction.", - "jsdocTags": [] + "description": "The index of this row within the grid.", + "jsdocTags": [], + "inputAlias": "rowIndex", + "isRequiredInput": false + } + ], + "generics": [], + "description": "Represents a row within a grid. It is a container for `ngGridCell` directives.\n\n```html\n\n \n\n```", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" }, { - "name": "enableSelection", - "type": "any", + "name": "see", + "comment": "[Grid](guide/aria/grid)" + } + ], + "rawComment": "/**\n * Represents a row within a grid. It is a container for `ngGridCell` directives.\n *\n * ```html\n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Grid](guide/aria/grid)\n */", + "implements": [], + "isStandalone": true, + "selector": "[ngGridRow]", + "exportAs": [ + "ngGridRow" + ], + "source": { + "filePath": "/src/aria/grid/grid-row.ts", + "startLine": 35, + "endLine": 74 + } + }, + { + "name": "GridCellWidget", + "isAbstract": false, + "entryType": "directive", + "members": [ + { + "name": "element", + "type": "HTMLElement", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "Whether selection is enabled for the grid.", - "jsdocTags": [], - "inputAlias": "enableSelection", - "isRequiredInput": false + "description": "A reference to the host element.", + "jsdocTags": [] }, { - "name": "disabled", - "type": "any", + "name": "active", + "type": "Signal", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "Whether the grid is disabled.", - "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false + "description": "Whether the widget is currently active (focused).", + "jsdocTags": [] }, { - "name": "softDisabled", - "type": "any", + "name": "id", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", + "description": "A unique identifier for the widget.", "jsdocTags": [], - "inputAlias": "softDisabled", + "inputAlias": "id", "isRequiredInput": false }, { - "name": "focusMode", - "type": "any", + "name": "widgetType", + "type": "InputSignal<\"simple\" | \"complex\" | \"editable\">", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The focus strategy used by the grid.\n- `roving`: Focus is moved to the active cell using `tabindex`.\n- `activedescendant`: Focus remains on the grid container, and `aria-activedescendant` is used to indicate the active cell.", + "description": "The type of widget, which determines how it is activated.", "jsdocTags": [], - "inputAlias": "focusMode", + "inputAlias": "widgetType", "isRequiredInput": false }, { - "name": "rowWrap", - "type": "any", + "name": "disabled", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The wrapping behavior for keyboard navigation along the row axis.\n- `continuous`: Navigation wraps from the last row to the first, and vice-versa.\n- `loop`: Navigation wraps within the current row.\n- `nowrap`: Navigation stops at the first/last item in the row.", + "description": "Whether the widget is disabled.", "jsdocTags": [], - "inputAlias": "rowWrap", + "inputAlias": "disabled", "isRequiredInput": false }, { - "name": "colWrap", - "type": "any", + "name": "focusTarget", + "type": "InputSignal | undefined>", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The wrapping behavior for keyboard navigation along the column axis.\n- `continuous`: Navigation wraps from the last column to the first, and vice-versa.\n- `loop`: Navigation wraps within the current column.\n- `nowrap`: Navigation stops at the first/last item in the column.", + "description": "The target that will receive focus instead of the widget.", "jsdocTags": [], - "inputAlias": "colWrap", + "inputAlias": "focusTarget", "isRequiredInput": false }, { - "name": "multi", - "type": "any", + "name": "onActivate", + "type": "OutputEmitterRef", "memberType": "property", "memberTags": [ "readonly", - "input" + "output" ], - "description": "Whether multiple cells in the grid can be selected.", + "description": "Emits when the widget is activated.", "jsdocTags": [], - "inputAlias": "multi", - "isRequiredInput": false + "outputAlias": "onActivate" }, { - "name": "selectionMode", - "type": "any", + "name": "onDeactivate", + "type": "OutputEmitterRef", "memberType": "property", "memberTags": [ "readonly", - "input" + "output" ], - "description": "The selection strategy used by the grid.\n- `follow`: The focused cell is automatically selected.\n- `explicit`: Cells are selected explicitly by the user (e.g., via click or spacebar).", + "description": "Emits when the widget is deactivated.", "jsdocTags": [], - "inputAlias": "selectionMode", - "isRequiredInput": false + "outputAlias": "onDeactivate" }, { - "name": "enableRangeSelection", - "type": "any", + "name": "tabindex", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Whether enable range selections (with modifier keys or dragging).", + "description": "The tabindex override.", "jsdocTags": [], - "inputAlias": "enableRangeSelection", + "inputAlias": "tabindex", "isRequiredInput": false - } - ], - "generics": [], - "description": "The container for a grid. It provides keyboard navigation and focus management for the grid's\nrows and cells. It manages the overall behavior of the grid, including focus\nwrapping, selection, and disabled states.\n\n```html\n", - "jsdocTags": [ - { - "name": "for", - "comment": "(row of gridData; track row) {\n" }, { - "name": "for", - "comment": "(cell of row; track cell) {\n \n}\n\n}\n
    \n {{cell.value}}\n
    \n```" + "name": "isActivated", + "type": "Signal", + "memberType": "getter", + "memberTags": [], + "description": "Whether the widget is activated.", + "jsdocTags": [] }, { - "name": "developerPreview", - "comment": "21.0" - } - ], - "rawComment": "/**\n * The container for a grid. It provides keyboard navigation and focus management for the grid's\n * rows and cells. It manages the overall behavior of the grid, including focus\n * wrapping, selection, and disabled states.\n *\n * ```html\n * \n * @for (row of gridData; track row) {\n * \n * @for (cell of row; track cell) {\n * \n * }\n * \n * }\n *
    \n * {{cell.value}}\n *
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], - "isStandalone": true, - "selector": "[ngGrid]", - "exportAs": [ - "ngGrid" - ], - "source": { - "filePath": "src/aria/grid/grid.ts", - "startLine": 47, - "endLine": 163 - } - }, - { - "name": "GridRow", - "isAbstract": false, - "entryType": "directive", - "members": [ - { - "name": "element", - "type": "HTMLElement", - "memberType": "property", - "memberTags": [ - "readonly" + "name": "activate", + "signatures": [ + { + "name": "activate", + "entryType": "function", + "description": "Activates the widget.", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [], + "rawComment": "/** Activates the widget. */", + "returnType": "void" + } ], - "description": "A reference to the host element.", - "jsdocTags": [] + "implementation": { + "params": [], + "isNewType": false, + "returnType": "void", + "generics": [], + "name": "activate", + "description": "Activates the widget.", + "entryType": "function", + "jsdocTags": [], + "rawComment": "/** Activates the widget. */" + }, + "entryType": "function", + "description": "Activates the widget.", + "jsdocTags": [], + "rawComment": "/** Activates the widget. */", + "memberType": "method", + "memberTags": [] }, { - "name": "rowIndex", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" + "name": "deactivate", + "signatures": [ + { + "name": "deactivate", + "entryType": "function", + "description": "Deactivates the widget.", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [], + "rawComment": "/** Deactivates the widget. */", + "returnType": "void" + } ], - "description": "The index of this row within the grid.", + "implementation": { + "params": [], + "isNewType": false, + "returnType": "void", + "generics": [], + "name": "deactivate", + "description": "Deactivates the widget.", + "entryType": "function", + "jsdocTags": [], + "rawComment": "/** Deactivates the widget. */" + }, + "entryType": "function", + "description": "Deactivates the widget.", "jsdocTags": [], - "inputAlias": "rowIndex", - "isRequiredInput": false + "rawComment": "/** Deactivates the widget. */", + "memberType": "method", + "memberTags": [] } ], "generics": [], - "description": "Represents a row within a grid. It is a container for `ngGridCell` directives.\n\n```html\n\n \n\n```", + "description": "Represents an interactive element inside a `GridCell`. It allows for pausing grid navigation to\ninteract with the widget.\n\nWhen the user interacts with the widget (e.g., by typing in an input or opening a menu), grid\nnavigation is temporarily suspended to allow the widget to handle keyboard\nevents.\n\n```html\n\n \n\n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Grid](guide/aria/grid)" } ], - "rawComment": "/**\n * Represents a row within a grid. It is a container for `ngGridCell` directives.\n *\n * ```html\n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * Represents an interactive element inside a `GridCell`. It allows for pausing grid navigation to\n * interact with the widget.\n *\n * When the user interacts with the widget (e.g., by typing in an input or opening a menu), grid\n * navigation is temporarily suspended to allow the widget to handle keyboard\n * events.\n *\n * ```html\n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Grid](guide/aria/grid)\n */", "implements": [], "isStandalone": true, - "selector": "[ngGridRow]", + "selector": "[ngGridCellWidget]", "exportAs": [ - "ngGridRow" + "ngGridCellWidget" ], "source": { - "filePath": "src/aria/grid/grid.ts", - "startLine": 176, - "endLine": 214 + "filePath": "src/aria/grid/grid-cell-widget.ts", + "startLine": 42, + "endLine": 136 } }, { - "name": "GridCell", + "name": "Grid", "isAbstract": false, "entryType": "directive", "members": [ @@ -241,19 +287,9 @@ "description": "A reference to the host element.", "jsdocTags": [] }, - { - "name": "active", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the cell is currently active (focused).", - "jsdocTags": [] - }, { "name": "textDirection", - "type": "any", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" @@ -262,187 +298,150 @@ "jsdocTags": [] }, { - "name": "id", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "A unique identifier for the cell.", - "jsdocTags": [], - "inputAlias": "id", - "isRequiredInput": false - }, - { - "name": "role", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "The ARIA role for the cell.", - "jsdocTags": [], - "inputAlias": "role", - "isRequiredInput": false - }, - { - "name": "rowSpan", - "type": "any", + "name": "enableSelection", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The number of rows the cell should span.", + "description": "Whether selection is enabled for the grid.", "jsdocTags": [], - "inputAlias": "rowSpan", + "inputAlias": "enableSelection", "isRequiredInput": false }, { - "name": "colSpan", - "type": "any", + "name": "disabled", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The number of columns the cell should span.", + "description": "Whether the grid is disabled.", "jsdocTags": [], - "inputAlias": "colSpan", + "inputAlias": "disabled", "isRequiredInput": false }, { - "name": "rowIndex", - "type": "any", + "name": "softDisabled", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The index of this cell's row within the grid.", + "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", "jsdocTags": [], - "inputAlias": "rowIndex", + "inputAlias": "softDisabled", "isRequiredInput": false }, { - "name": "colIndex", - "type": "any", + "name": "focusMode", + "type": "InputSignal<\"roving\" | \"activedescendant\">", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The index of this cell's column within the grid.", + "description": "The focus strategy used by the grid.\n- `roving`: Focus is moved to the active cell using `tabindex`.\n- `activedescendant`: Focus remains on the grid container, and `aria-activedescendant` is used to indicate the active cell.", "jsdocTags": [], - "inputAlias": "colIndex", + "inputAlias": "focusMode", "isRequiredInput": false }, { - "name": "disabled", - "type": "any", + "name": "rowWrap", + "type": "InputSignal<\"continuous\" | \"loop\" | \"nowrap\">", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Whether the cell is disabled.", + "description": "The wrapping behavior for keyboard navigation along the row axis.\n- `continuous`: Navigation wraps from the last row to the first, and vice-versa.\n- `loop`: Navigation wraps within the current row.\n- `nowrap`: Navigation stops at the first/last item in the row.", "jsdocTags": [], - "inputAlias": "disabled", + "inputAlias": "rowWrap", "isRequiredInput": false }, { - "name": "selected", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input", - "output" - ], - "description": "Whether the cell is selected.", - "jsdocTags": [], - "inputAlias": "selected", - "isRequiredInput": false, - "outputAlias": "selectedChange" - }, - { - "name": "selectable", - "type": "any", + "name": "colWrap", + "type": "InputSignal<\"continuous\" | \"loop\" | \"nowrap\">", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Whether the cell is selectable.", + "description": "The wrapping behavior for keyboard navigation along the column axis.\n- `continuous`: Navigation wraps from the last column to the first, and vice-versa.\n- `loop`: Navigation wraps within the current column.\n- `nowrap`: Navigation stops at the first/last item in the column.", "jsdocTags": [], - "inputAlias": "selectable", + "inputAlias": "colWrap", "isRequiredInput": false }, { - "name": "orientation", - "type": "any", + "name": "multi", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Orientation of the widgets in the cell.", + "description": "Whether multiple cells in the grid can be selected.", "jsdocTags": [], - "inputAlias": "orientation", + "inputAlias": "multi", "isRequiredInput": false }, { - "name": "wrap", - "type": "any", + "name": "selectionMode", + "type": "InputSignal<\"follow\" | \"explicit\">", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "Whether widgets navigation wraps.", + "description": "The selection strategy used by the grid.\n- `follow`: The focused cell is automatically selected.\n- `explicit`: Cells are selected explicitly by the user (e.g., via click or spacebar).", "jsdocTags": [], - "inputAlias": "wrap", + "inputAlias": "selectionMode", "isRequiredInput": false }, { - "name": "tabindex", - "type": "any", + "name": "enableRangeSelection", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The tabindex override.", + "description": "Whether enable range selections (with modifier keys or dragging).", "jsdocTags": [], - "inputAlias": "tabindex", + "inputAlias": "enableRangeSelection", "isRequiredInput": false } ], "generics": [], - "description": "Represents a cell within a grid row. It is the primary focusable element\nwithin the grid. It can be disabled and can have its selection state managed\nthrough the `selected` input.\n\n```html\n\n Cell Content\n\n```", + "description": "The container for a grid. It provides keyboard navigation and focus management for the grid's\nrows and cells. It manages the overall behavior of the grid, including focus\nwrapping, selection, and disabled states.\n\n```html\n\n @for (row of gridData; track row) {\n \n @for (cell of row; track cell) {\n \n }\n \n }\n
    \n {{cell.value}}\n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Grid](guide/aria/grid)" } ], - "rawComment": "/**\n * Represents a cell within a grid row. It is the primary focusable element\n * within the grid. It can be disabled and can have its selection state managed\n * through the `selected` input.\n *\n * ```html\n * \n * Cell Content\n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * The container for a grid. It provides keyboard navigation and focus management for the grid's\n * rows and cells. It manages the overall behavior of the grid, including focus\n * wrapping, selection, and disabled states.\n *\n * ```html\n * \n * @for (row of gridData; track row) {\n * \n * @for (cell of row; track cell) {\n * \n * }\n * \n * }\n *
    \n * {{cell.value}}\n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Grid](guide/aria/grid)\n */", "implements": [], "isStandalone": true, - "selector": "[ngGridCell]", + "selector": "[ngGrid]", "exportAs": [ - "ngGridCell" + "ngGrid" ], "source": { - "filePath": "src/aria/grid/grid.ts", - "startLine": 229, - "endLine": 343 + "filePath": "/src/aria/grid/grid.ts", + "startLine": 47, + "endLine": 161 } }, { - "name": "GridCellWidget", + "name": "GridCell", "isAbstract": false, "entryType": "directive", "members": [ @@ -458,93 +457,172 @@ }, { "name": "active", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the widget is currently active (focused).", + "description": "Whether the cell is currently active (focused).", "jsdocTags": [] }, { - "name": "id", - "type": "any", + "name": "textDirection", + "type": "WritableSignal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Text direction.", + "jsdocTags": [] + }, + { + "name": "id", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "A unique identifier for the cell.", + "jsdocTags": [], + "inputAlias": "id", + "isRequiredInput": false + }, + { + "name": "role", + "type": "InputSignal<\"gridcell\" | \"columnheader\" | \"rowheader\">", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The ARIA role for the cell.", + "jsdocTags": [], + "inputAlias": "role", + "isRequiredInput": false + }, + { + "name": "rowSpan", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The number of rows the cell should span.", + "jsdocTags": [], + "inputAlias": "rowSpan", + "isRequiredInput": false + }, + { + "name": "colSpan", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The number of columns the cell should span.", + "jsdocTags": [], + "inputAlias": "colSpan", + "isRequiredInput": false + }, + { + "name": "rowIndex", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The index of this cell's row within the grid.", + "jsdocTags": [], + "inputAlias": "rowIndex", + "isRequiredInput": false + }, + { + "name": "colIndex", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "A unique identifier for the widget.", + "description": "The index of this cell's column within the grid.", "jsdocTags": [], - "inputAlias": "id", + "inputAlias": "colIndex", "isRequiredInput": false }, { - "name": "widgetType", - "type": "any", + "name": "disabled", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The type of widget, which determines how it is activated.", + "description": "Whether the cell is disabled.", "jsdocTags": [], - "inputAlias": "widgetType", + "inputAlias": "disabled", "isRequiredInput": false }, { - "name": "disabled", - "type": "any", + "name": "selected", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "readonly", - "input" + "input", + "output" ], - "description": "Whether the widget is disabled.", + "description": "Whether the cell is selected.", "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false + "inputAlias": "selected", + "isRequiredInput": false, + "outputAlias": "selectedChange" }, { - "name": "focusTarget", - "type": "any", + "name": "selectable", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", "input" ], - "description": "The target that will receive focus instead of the widget.", + "description": "Whether the cell is selectable.", "jsdocTags": [], - "inputAlias": "focusTarget", + "inputAlias": "selectable", "isRequiredInput": false }, { - "name": "onActivate", - "type": "any", + "name": "orientation", + "type": "InputSignal<\"vertical\" | \"horizontal\">", "memberType": "property", "memberTags": [ "readonly", - "output" + "input" ], - "description": "Emits when the widget is activated.", + "description": "Orientation of the widgets in the cell.", "jsdocTags": [], - "outputAlias": "onActivate" + "inputAlias": "orientation", + "isRequiredInput": false }, { - "name": "onDeactivate", - "type": "any", + "name": "wrap", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", - "output" + "input" ], - "description": "Emits when the widget is deactivated.", + "description": "Whether widgets navigation wraps.", "jsdocTags": [], - "outputAlias": "onDeactivate" + "inputAlias": "wrap", + "isRequiredInput": false }, { "name": "tabindex", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -554,117 +632,35 @@ "jsdocTags": [], "inputAlias": "tabindex", "isRequiredInput": false - }, - { - "name": "isActivated", - "type": "Signal", - "memberType": "getter", - "memberTags": [], - "description": "Whether the widget is activated.", - "jsdocTags": [] - }, - { - "name": "activate", - "signatures": [ - { - "name": "activate", - "entryType": "function", - "description": "Activates the widget.", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [], - "rawComment": "/** Activates the widget. */", - "returnType": "void" - } - ], - "implementation": { - "params": [], - "isNewType": false, - "returnType": "void", - "generics": [], - "name": "activate", - "description": "Activates the widget.", - "entryType": "function", - "jsdocTags": [], - "rawComment": "/** Activates the widget. */" - }, - "entryType": "function", - "description": "Activates the widget.", - "jsdocTags": [], - "rawComment": "/** Activates the widget. */", - "memberType": "method", - "memberTags": [] - }, - { - "name": "deactivate", - "signatures": [ - { - "name": "deactivate", - "entryType": "function", - "description": "Deactivates the widget.", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [], - "rawComment": "/** Deactivates the widget. */", - "returnType": "void" - } - ], - "implementation": { - "params": [], - "isNewType": false, - "returnType": "void", - "generics": [], - "name": "deactivate", - "description": "Deactivates the widget.", - "entryType": "function", - "jsdocTags": [], - "rawComment": "/** Deactivates the widget. */" - }, - "entryType": "function", - "description": "Deactivates the widget.", - "jsdocTags": [], - "rawComment": "/** Deactivates the widget. */", - "memberType": "method", - "memberTags": [] } ], "generics": [], - "description": "Represents an interactive element inside a `GridCell`. It allows for pausing grid navigation to\ninteract with the widget.\n\nWhen the user interacts with the widget (e.g., by typing in an input or opening a menu), grid\nnavigation is temporarily suspended to allow the widget to handle keyboard\nevents.\n\n```html\n\n \n\n```", + "description": "Represents a cell within a grid row. It is the primary focusable element\nwithin the grid. It can be disabled and can have its selection state managed\nthrough the `selected` input.\n\n```html\n\n Cell Content\n\n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Grid](guide/aria/grid)" } ], - "rawComment": "/**\n * Represents an interactive element inside a `GridCell`. It allows for pausing grid navigation to\n * interact with the widget.\n *\n * When the user interacts with the widget (e.g., by typing in an input or opening a menu), grid\n * navigation is temporarily suspended to allow the widget to handle keyboard\n * events.\n *\n * ```html\n * \n * \n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * Represents a cell within a grid row. It is the primary focusable element\n * within the grid. It can be disabled and can have its selection state managed\n * through the `selected` input.\n *\n * ```html\n * \n * Cell Content\n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Grid](guide/aria/grid)\n */", "implements": [], "isStandalone": true, - "selector": "[ngGridCellWidget]", + "selector": "[ngGridCell]", "exportAs": [ - "ngGridCellWidget" + "ngGridCell" ], "source": { - "filePath": "src/aria/grid/grid.ts", - "startLine": 361, - "endLine": 455 + "filePath": "/src/aria/grid/grid-cell.ts", + "startLine": 41, + "endLine": 156 } } ], "symbols": [ - [ - "_IdGenerator", - "@angular/cdk/a11y" - ], - [ - "afterRenderEffect", - "@angular/core" - ], - [ - "booleanAttribute", - "@angular/core" - ], [ "computed", "@angular/core" @@ -690,15 +686,23 @@ "@angular/core" ], [ - "output", + "Signal", "@angular/core" ], [ - "model", + "_IdGenerator", + "@angular/cdk/a11y" + ], + [ + "afterRenderEffect", "@angular/core" ], [ - "Signal", + "booleanAttribute", + "@angular/core" + ], + [ + "output", "@angular/core" ], [ @@ -706,195 +710,199 @@ "@angular/cdk/bidi" ], [ - "Grid", - "@angular/aria/grid" + "model", + "@angular/core" ], [ "GridRow", "@angular/aria/grid" ], [ - "GridCell", + "GridCellWidget", "@angular/aria/grid" ], [ - "GridCellWidget", + "Grid", "@angular/aria/grid" ], [ - "Grid", + "GridCell", "@angular/aria/grid" ], [ - "Grid.element", + "GridRow", "@angular/aria/grid" ], [ - "Grid.textDirection", + "GridRow.element", "@angular/aria/grid" ], [ - "Grid.enableSelection", + "GridRow.rowIndex", "@angular/aria/grid" ], [ - "Grid.disabled", + "GridCellWidget", "@angular/aria/grid" ], [ - "Grid.softDisabled", + "GridCellWidget.element", "@angular/aria/grid" ], [ - "Grid.focusMode", + "GridCellWidget.active", "@angular/aria/grid" ], [ - "Grid.rowWrap", + "GridCellWidget.id", "@angular/aria/grid" ], [ - "Grid.colWrap", + "GridCellWidget.widgetType", "@angular/aria/grid" ], [ - "Grid.multi", + "GridCellWidget.disabled", "@angular/aria/grid" ], [ - "Grid.selectionMode", + "GridCellWidget.focusTarget", "@angular/aria/grid" ], [ - "Grid.enableRangeSelection", + "GridCellWidget.onActivate", "@angular/aria/grid" ], [ - "GridRow", + "GridCellWidget.onDeactivate", "@angular/aria/grid" ], [ - "GridRow.element", + "GridCellWidget.tabindex", "@angular/aria/grid" ], [ - "GridRow.rowIndex", + "GridCellWidget.isActivated", "@angular/aria/grid" ], [ - "GridCell", + "GridCellWidget.activate", "@angular/aria/grid" ], [ - "GridCell.element", + "GridCellWidget.deactivate", "@angular/aria/grid" ], [ - "GridCell.active", + "Grid", "@angular/aria/grid" ], [ - "GridCell.textDirection", + "Grid.element", "@angular/aria/grid" ], [ - "GridCell.id", + "Grid.textDirection", "@angular/aria/grid" ], [ - "GridCell.role", + "Grid.enableSelection", "@angular/aria/grid" ], [ - "GridCell.rowSpan", + "Grid.disabled", "@angular/aria/grid" ], [ - "GridCell.colSpan", + "Grid.softDisabled", "@angular/aria/grid" ], [ - "GridCell.rowIndex", + "Grid.focusMode", "@angular/aria/grid" ], [ - "GridCell.colIndex", + "Grid.rowWrap", "@angular/aria/grid" ], [ - "GridCell.disabled", + "Grid.colWrap", "@angular/aria/grid" ], [ - "GridCell.selected", + "Grid.multi", "@angular/aria/grid" ], [ - "GridCell.selectable", + "Grid.selectionMode", "@angular/aria/grid" ], [ - "GridCell.orientation", + "Grid.enableRangeSelection", "@angular/aria/grid" ], [ - "GridCell.wrap", + "GridCell", "@angular/aria/grid" ], [ - "GridCell.tabindex", + "GridCell.element", "@angular/aria/grid" ], [ - "GridCellWidget", + "GridCell.active", "@angular/aria/grid" ], [ - "GridCellWidget.element", + "GridCell.textDirection", "@angular/aria/grid" ], [ - "GridCellWidget.active", + "GridCell.id", "@angular/aria/grid" ], [ - "GridCellWidget.id", + "GridCell.role", "@angular/aria/grid" ], [ - "GridCellWidget.widgetType", + "GridCell.rowSpan", "@angular/aria/grid" ], [ - "GridCellWidget.disabled", + "GridCell.colSpan", "@angular/aria/grid" ], [ - "GridCellWidget.focusTarget", + "GridCell.rowIndex", "@angular/aria/grid" ], [ - "GridCellWidget.onActivate", + "GridCell.colIndex", "@angular/aria/grid" ], [ - "GridCellWidget.onDeactivate", + "GridCell.disabled", "@angular/aria/grid" ], [ - "GridCellWidget.tabindex", + "GridCell.selected", "@angular/aria/grid" ], [ - "GridCellWidget.isActivated", + "GridCell.selectable", "@angular/aria/grid" ], [ - "GridCellWidget.activate", + "GridCell.orientation", "@angular/aria/grid" ], [ - "GridCellWidget.deactivate", + "GridCell.wrap", + "@angular/aria/grid" + ], + [ + "GridCell.tabindex", "@angular/aria/grid" ] ] diff --git a/adev/src/content/aria/aria-listbox.json b/adev/src/content/aria/aria-listbox.json index 2385e25650e1..4f0042ed8b6f 100755 --- a/adev/src/content/aria/aria-listbox.json +++ b/adev/src/content/aria/aria-listbox.json @@ -4,6 +4,140 @@ "moduleName": "@angular/aria/listbox", "normalizedModuleName": "angular_aria_listbox", "entries": [ + { + "name": "Option", + "isAbstract": false, + "entryType": "directive", + "members": [ + { + "name": "element", + "type": "HTMLElement", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "A reference to the host element.", + "jsdocTags": [] + }, + { + "name": "active", + "type": "Signal", + "memberType": "property", + "memberTags": [], + "description": "Whether the option is currently active (focused).", + "jsdocTags": [] + }, + { + "name": "id", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "A unique identifier for the option.", + "jsdocTags": [], + "inputAlias": "id", + "isRequiredInput": false + }, + { + "name": "searchTerm", + "type": "Signal", + "memberType": "property", + "memberTags": [ + "protected" + ], + "description": "The text used by the typeahead search.", + "jsdocTags": [] + }, + { + "name": "value", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "input" + ], + "description": "The value of the option.", + "jsdocTags": [], + "inputAlias": "value", + "isRequiredInput": true + }, + { + "name": "disabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "input" + ], + "description": "Whether an item is disabled.", + "jsdocTags": [], + "inputAlias": "disabled", + "isRequiredInput": false + }, + { + "name": "label", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "input" + ], + "description": "The text used by the typeahead search.", + "jsdocTags": [], + "inputAlias": "label", + "isRequiredInput": false + }, + { + "name": "selected", + "type": "Signal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the option is selected.", + "jsdocTags": [] + } + ], + "generics": [ + { + "name": "V" + } + ], + "description": "A selectable option in an `ngListbox`.\n\nThis directive should be applied to an element (e.g., `
  • `, `
    `) within an\n`ngListbox`. The `value` input is used to identify the option, and the `label` input provides\nthe accessible name for the option.\n\n```html\n
  • \n Item Name\n
  • \n```", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" + }, + { + "name": "see", + "comment": "[Listbox](guide/aria/listbox)" + }, + { + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" + }, + { + "name": "see", + "comment": "[Select](guide/aria/select)" + }, + { + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" + } + ], + "rawComment": "/**\n * A selectable option in an `ngListbox`.\n *\n * This directive should be applied to an element (e.g., `
  • `, `
    `) within an\n * `ngListbox`. The `value` input is used to identify the option, and the `label` input provides\n * the accessible name for the option.\n *\n * ```html\n *
  • \n * Item Name\n *
  • \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Listbox](guide/aria/listbox)\n * @see [Autocomplete](guide/aria/autocomplete)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n */", + "implements": [], + "isStandalone": true, + "selector": "[ngOption]", + "exportAs": [ + "ngOption" + ], + "source": { + "filePath": "/src/aria/listbox/option.ts", + "startLine": 34, + "endLine": 88 + } + }, { "name": "Listbox", "isAbstract": false, @@ -11,7 +145,7 @@ "members": [ { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly" @@ -31,7 +165,7 @@ }, { "name": "textDirection", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "protected" @@ -41,7 +175,7 @@ }, { "name": "items", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "protected" @@ -51,7 +185,7 @@ }, { "name": "orientation", - "type": "any", + "type": "InputSignal<\"vertical\" | \"horizontal\">", "memberType": "property", "memberTags": [], "description": "Whether the list is vertically or horizontally oriented.", @@ -59,7 +193,7 @@ }, { "name": "multi", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [], "description": "Whether multiple items in the list can be selected at once.", @@ -67,7 +201,7 @@ }, { "name": "wrap", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [], "description": "Whether focus should wrap when navigating.", @@ -75,7 +209,7 @@ }, { "name": "softDisabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [], "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", @@ -83,7 +217,7 @@ }, { "name": "focusMode", - "type": "any", + "type": "InputSignal<\"roving\" | \"activedescendant\">", "memberType": "property", "memberTags": [], "description": "The focus strategy used by the list.\n- `roving`: Focus is moved to the active item using `tabindex`.\n- `activedescendant`: Focus remains on the listbox container, and `aria-activedescendant` is used to indicate the active item.", @@ -91,7 +225,7 @@ }, { "name": "selectionMode", - "type": "any", + "type": "InputSignal<\"follow\" | \"explicit\">", "memberType": "property", "memberTags": [], "description": "The selection strategy used by the list.\n- `follow`: The focused item is automatically selected.\n- `explicit`: Items are selected explicitly by the user (e.g., via click or spacebar).", @@ -99,7 +233,7 @@ }, { "name": "typeaheadDelay", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [], "description": "The amount of time before the typeahead search is reset.", @@ -107,7 +241,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [], "description": "Whether the listbox is disabled.", @@ -115,7 +249,7 @@ }, { "name": "readonly", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [], "description": "Whether the listbox is readonly.", @@ -123,7 +257,7 @@ }, { "name": "values", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [], "description": "The values of the currently selected items.", @@ -217,149 +351,39 @@ "name": "V" } ], - "description": "Represents a container used to display a list of items for a user to select from.\n\nThe `ngListbox` is meant to be used in conjunction with `ngOption` directives to create a\nselectable list. It supports single and multiple selection modes, as well as various focus and\norientation strategies.\n\n```html\n
      ", + "description": "Represents a container used to display a list of items for a user to select from.\n\nThe `ngListbox` is meant to be used in conjunction with `ngOption` directives to create a\nselectable list. It supports single and multiple selection modes, as well as various focus and\norientation strategies.\n\n```html\n
        \n @for (item of items; track item.id) {\n
      • \n {{item.name}}\n
      • \n }\n
      \n```", "jsdocTags": [ - { - "name": "for", - "comment": "(item of items; track item.id) {\n
    • \n{{item.name}}\n
    • \n}\n
    \n```" - }, { "name": "developerPreview", "comment": "21.0" - } - ], - "rawComment": "/**\n * Represents a container used to display a list of items for a user to select from.\n *\n * The `ngListbox` is meant to be used in conjunction with `ngOption` directives to create a\n * selectable list. It supports single and multiple selection modes, as well as various focus and\n * orientation strategies.\n *\n * ```html\n *
      \n * @for (item of items; track item.id) {\n *
    • \n * {{item.name}}\n *
    • \n * }\n *
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], - "source": { - "filePath": "/src/aria/listbox/listbox.ts", - "startLine": 47, - "endLine": 209 - } - }, - { - "name": "Option", - "isAbstract": false, - "entryType": "directive", - "members": [ - { - "name": "element", - "type": "HTMLElement", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "A reference to the host element.", - "jsdocTags": [] }, { - "name": "active", - "type": "any", - "memberType": "property", - "memberTags": [], - "description": "Whether the option is currently active (focused).", - "jsdocTags": [] + "name": "see", + "comment": "[Listbox](guide/aria/listbox)" }, { - "name": "id", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "A unique identifier for the option.", - "jsdocTags": [], - "inputAlias": "id", - "isRequiredInput": false - }, - { - "name": "searchTerm", - "type": "any", - "memberType": "property", - "memberTags": [ - "protected" - ], - "description": "The text used by the typeahead search.", - "jsdocTags": [] - }, - { - "name": "value", - "type": "any", - "memberType": "property", - "memberTags": [ - "input" - ], - "description": "The value of the option.", - "jsdocTags": [], - "inputAlias": "value", - "isRequiredInput": true + "name": "see", + "comment": "[Autocomplete](guide/aria/autocomplete)" }, { - "name": "disabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "input" - ], - "description": "Whether an item is disabled.", - "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false + "name": "see", + "comment": "[Select](guide/aria/select)" }, { - "name": "label", - "type": "any", - "memberType": "property", - "memberTags": [ - "input" - ], - "description": "The text used by the typeahead search.", - "jsdocTags": [], - "inputAlias": "label", - "isRequiredInput": false - }, - { - "name": "selected", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the option is selected.", - "jsdocTags": [] + "name": "see", + "comment": "[Multiselect](guide/aria/multiselect)" } ], - "generics": [ - { - "name": "V" - } - ], - "description": "A selectable option in an `ngListbox`.\n\nThis directive should be applied to an element (e.g., `
  • `, `
    `) within an\n`ngListbox`. The `value` input is used to identify the option, and the `label` input provides\nthe accessible name for the option.\n\n```html\n
  • \n Item Name\n
  • \n```", - "jsdocTags": [ - { - "name": "developerPreview", - "comment": "21.0" - } - ], - "rawComment": "/**\n * A selectable option in an `ngListbox`.\n *\n * This directive should be applied to an element (e.g., `
  • `, `
    `) within an\n * `ngListbox`. The `value` input is used to identify the option, and the `label` input provides\n * the accessible name for the option.\n *\n * ```html\n *
  • \n * Item Name\n *
  • \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * Represents a container used to display a list of items for a user to select from.\n *\n * The `ngListbox` is meant to be used in conjunction with `ngOption` directives to create a\n * selectable list. It supports single and multiple selection modes, as well as various focus and\n * orientation strategies.\n *\n * ```html\n *
      \n * @for (item of items; track item.id) {\n *
    • \n * {{item.name}}\n *
    • \n * }\n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Listbox](guide/aria/listbox)\n * @see [Autocomplete](guide/aria/autocomplete)\n * @see [Select](guide/aria/select)\n * @see [Multiselect](guide/aria/multiselect)\n */", "implements": [], - "isStandalone": true, - "selector": "[ngOption]", - "exportAs": [ - "ngOption" - ], "source": { "filePath": "/src/aria/listbox/listbox.ts", - "startLine": 226, - "endLine": 283 + "startLine": 53, + "endLine": 211 } } ], "symbols": [ - [ - "afterRenderEffect", - "@angular/core" - ], [ "booleanAttribute", "@angular/core" @@ -368,10 +392,6 @@ "computed", "@angular/core" ], - [ - "contentChildren", - "@angular/core" - ], [ "Directive", "@angular/core" @@ -389,40 +409,36 @@ "@angular/core" ], [ - "model", - "@angular/core" + "_IdGenerator", + "@angular/cdk/a11y" ], [ - "signal", + "afterRenderEffect", "@angular/core" ], [ - "untracked", + "contentChildren", "@angular/core" ], [ - "ComboboxListboxPattern", - "@angular/aria/private" + "model", + "@angular/core" ], [ - "ListboxPattern", - "@angular/aria/private" + "signal", + "@angular/core" ], [ - "OptionPattern", - "@angular/aria/private" + "untracked", + "@angular/core" ], [ "Directionality", "@angular/cdk/bidi" ], [ - "toSignal", - "@angular/core/rxjs-interop" - ], - [ - "_IdGenerator", - "@angular/cdk/a11y" + "Option", + "@angular/aria/listbox" ], [ "Listbox", @@ -433,107 +449,103 @@ "@angular/aria/listbox" ], [ - "Listbox", - "@angular/aria/listbox" - ], - [ - "Listbox.id", + "Option.element", "@angular/aria/listbox" ], [ - "Listbox.element", + "Option.active", "@angular/aria/listbox" ], [ - "Listbox.textDirection", + "Option.id", "@angular/aria/listbox" ], [ - "Listbox.items", + "Option.searchTerm", "@angular/aria/listbox" ], [ - "Listbox.orientation", + "Option.value", "@angular/aria/listbox" ], [ - "Listbox.multi", + "Option.disabled", "@angular/aria/listbox" ], [ - "Listbox.wrap", + "Option.label", "@angular/aria/listbox" ], [ - "Listbox.softDisabled", + "Option.selected", "@angular/aria/listbox" ], [ - "Listbox.focusMode", + "Listbox", "@angular/aria/listbox" ], [ - "Listbox.selectionMode", + "Listbox.id", "@angular/aria/listbox" ], [ - "Listbox.typeaheadDelay", + "Listbox.element", "@angular/aria/listbox" ], [ - "Listbox.disabled", + "Listbox.textDirection", "@angular/aria/listbox" ], [ - "Listbox.readonly", + "Listbox.items", "@angular/aria/listbox" ], [ - "Listbox.values", + "Listbox.orientation", "@angular/aria/listbox" ], [ - "Listbox.scrollActiveItemIntoView", + "Listbox.multi", "@angular/aria/listbox" ], [ - "Listbox.gotoFirst", + "Listbox.wrap", "@angular/aria/listbox" ], [ - "Option", + "Listbox.softDisabled", "@angular/aria/listbox" ], [ - "Option.element", + "Listbox.focusMode", "@angular/aria/listbox" ], [ - "Option.active", + "Listbox.selectionMode", "@angular/aria/listbox" ], [ - "Option.id", + "Listbox.typeaheadDelay", "@angular/aria/listbox" ], [ - "Option.searchTerm", + "Listbox.disabled", "@angular/aria/listbox" ], [ - "Option.value", + "Listbox.readonly", "@angular/aria/listbox" ], [ - "Option.disabled", + "Listbox.values", "@angular/aria/listbox" ], [ - "Option.label", + "Listbox.scrollActiveItemIntoView", "@angular/aria/listbox" ], [ - "Option.selected", + "Listbox.gotoFirst", "@angular/aria/listbox" ] ] diff --git a/adev/src/content/aria/aria-menu.json b/adev/src/content/aria/aria-menu.json index dfc356ca30e8..a33d5afa0326 100755 --- a/adev/src/content/aria/aria-menu.json +++ b/adev/src/content/aria/aria-menu.json @@ -4,6 +4,35 @@ "moduleName": "@angular/aria/menu", "normalizedModuleName": "angular_aria_menu", "entries": [ + { + "name": "MenuContent", + "isAbstract": false, + "entryType": "undecorated_class", + "members": [], + "generics": [], + "description": "Defers the rendering of the menu content.\n\nThis structural directive should be applied to an `ng-template` within a `ngMenu`\nor `ngMenuBar` to lazily render its content only when the menu is opened.\n\n```html\n
    \n \n
    Lazy Item 1
    \n
    Lazy Item 2
    \n
    \n
    \n```", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" + }, + { + "name": "see", + "comment": "[Menu](guide/aria/menu)" + }, + { + "name": "see", + "comment": "[MenuBar](guide/aria/menubar)" + } + ], + "rawComment": "/**\n * Defers the rendering of the menu content.\n *\n * This structural directive should be applied to an `ng-template` within a `ngMenu`\n * or `ngMenuBar` to lazily render its content only when the menu is opened.\n *\n * ```html\n *
    \n * \n *
    Lazy Item 1
    \n *
    Lazy Item 2
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */", + "implements": [], + "source": { + "filePath": "/src/aria/menu/menu-content.ts", + "startLine": 32, + "endLine": 37 + } + }, { "name": "MenuTrigger", "isAbstract": false, @@ -21,7 +50,7 @@ }, { "name": "textDirection", - "type": "any", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" @@ -31,7 +60,7 @@ }, { "name": "menu", - "type": "any", + "type": "InputSignal | undefined>", "memberType": "property", "memberTags": [ "input" @@ -43,7 +72,7 @@ }, { "name": "expanded", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -53,7 +82,7 @@ }, { "name": "hasPopup", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -63,7 +92,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -76,7 +105,7 @@ }, { "name": "softDisabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -164,9 +193,17 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Menu](guide/aria/menu)" + }, + { + "name": "see", + "comment": "[MenuBar](guide/aria/menubar)" } ], - "rawComment": "/**\n * A trigger for a menu.\n *\n * The `ngMenuTrigger` directive is used to open and close menus. It can be applied to\n * any interactive element (e.g., a button) to associate it with a `ngMenu` instance.\n * It also supports linking to sub-menus when applied to a `ngMenuItem`.\n *\n * ```html\n * \n *\n *
    \n *
    Item 1
    \n *
    Item 2
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A trigger for a menu.\n *\n * The `ngMenuTrigger` directive is used to open and close menus. It can be applied to\n * any interactive element (e.g., a button) to associate it with a `ngMenu` instance.\n * It also supports linking to sub-menus when applied to a `ngMenuItem`.\n *\n * ```html\n * \n *\n *
    \n *
    Item 1
    \n *
    Item 2
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */", "implements": [], "isStandalone": true, "selector": "button[ngMenuTrigger]", @@ -174,15 +211,15 @@ "ngMenuTrigger" ], "source": { - "filePath": "/src/aria/menu/menu.ts", - "startLine": 55, - "endLine": 117 + "filePath": "/src/aria/menu/menu-trigger.ts", + "startLine": 43, + "endLine": 105 } }, { - "name": "Menu", + "name": "MenuItem", "isAbstract": false, - "entryType": "undecorated_class", + "entryType": "directive", "members": [ { "name": "element", @@ -195,115 +232,157 @@ "jsdocTags": [] }, { - "name": "textDirection", - "type": "any", + "name": "id", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "The directionality (LTR / RTL) context for the application (or a subtree of it).", - "jsdocTags": [] + "description": "The unique ID of the menu item.", + "jsdocTags": [], + "inputAlias": "id", + "isRequiredInput": false }, { - "name": "id", - "type": "any", + "name": "value", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "The unique ID of the menu.", - "jsdocTags": [] + "description": "The value of the menu item.", + "jsdocTags": [], + "inputAlias": "value", + "isRequiredInput": true }, { - "name": "wrap", - "type": "any", + "name": "disabled", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "Whether the menu should wrap its items.", - "jsdocTags": [] + "description": "Whether the menu item is disabled.", + "jsdocTags": [], + "inputAlias": "disabled", + "isRequiredInput": false }, { - "name": "typeaheadDelay", - "type": "any", + "name": "searchTerm", + "type": "ModelSignal", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input", + "output" ], - "description": "The delay in milliseconds before the typeahead buffer is cleared.", - "jsdocTags": [] + "description": "The search term associated with the menu item.", + "jsdocTags": [], + "inputAlias": "searchTerm", + "isRequiredInput": false, + "outputAlias": "searchTermChange" }, { - "name": "disabled", - "type": "any", + "name": "parent", + "type": "Menu | MenuBar | null", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the menu is disabled.", + "description": "A reference to the parent menu or menubar.", "jsdocTags": [] }, { - "name": "parent", - "type": "any", + "name": "submenu", + "type": "InputSignal | undefined>", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "A reference to the parent menu item or menu trigger.", - "jsdocTags": [] + "description": "The submenu associated with the menu item.", + "jsdocTags": [], + "inputAlias": "submenu", + "isRequiredInput": false }, { - "name": "visible", - "type": "any", + "name": "active", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the menu is visible.", + "description": "Whether the menu item is active.", "jsdocTags": [] }, { - "name": "tabIndex", - "type": "any", + "name": "expanded", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "The tab index of the menu.", - "jsdocTags": [] - }, - { - "name": "onSelect", - "type": "any", - "memberType": "property", - "memberTags": [], - "description": "A callback function triggered when a menu item is selected.", + "description": "Whether the menu is expanded.", "jsdocTags": [] }, { - "name": "expansionDelay", - "type": "any", + "name": "hasPopup", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "The delay in milliseconds before expanding sub-menus on hover.", + "description": "Whether the menu item has a popup.", "jsdocTags": [] }, + { + "name": "open", + "signatures": [ + { + "name": "open", + "entryType": "function", + "description": "Opens the submenu focusing on the first menu item.", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [], + "rawComment": "/** Opens the submenu focusing on the first menu item. */", + "returnType": "void" + } + ], + "implementation": { + "params": [], + "isNewType": false, + "returnType": "void", + "generics": [], + "name": "open", + "description": "Opens the submenu focusing on the first menu item.", + "entryType": "function", + "jsdocTags": [], + "rawComment": "/** Opens the submenu focusing on the first menu item. */" + }, + "entryType": "function", + "description": "Opens the submenu focusing on the first menu item.", + "jsdocTags": [], + "rawComment": "/** Opens the submenu focusing on the first menu item. */", + "memberType": "method", + "memberTags": [] + }, { "name": "close", "signatures": [ { "name": "close", "entryType": "function", - "description": "Closes the menu.", + "description": "Closes the submenu.", "generics": [], "isNewType": false, "jsdocTags": [], "params": [], - "rawComment": "/** Closes the menu. */", + "rawComment": "/** Closes the submenu. */", "returnType": "void" } ], @@ -313,15 +392,15 @@ "returnType": "void", "generics": [], "name": "close", - "description": "Closes the menu.", + "description": "Closes the submenu.", "entryType": "function", "jsdocTags": [], - "rawComment": "/** Closes the menu. */" + "rawComment": "/** Closes the submenu. */" }, "entryType": "function", - "description": "Closes the menu.", + "description": "Closes the submenu.", "jsdocTags": [], - "rawComment": "/** Closes the menu. */", + "rawComment": "/** Closes the submenu. */", "memberType": "method", "memberTags": [] } @@ -331,19 +410,32 @@ "name": "V" } ], - "description": "A list of menu items.\n\nA `ngMenu` is used to offer a list of menu item choices to users. Menus can be nested\nwithin other menus to create sub-menus. It works in conjunction with `ngMenuTrigger`\nand `ngMenuItem` directives.\n\n```html\n\n\n
    \n
    Star
    \n
    Edit
    \n
    More
    \n
    \n\n
    \n
    Sub Item 1
    \n
    Sub Item 2
    \n
    \n```", + "description": "An item in a Menu.\n\n`ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\nor action a user can take. They can also act as triggers for sub-menus.\n\n```html\n
    Action Item
    \n\n
    Submenu Trigger
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Menu](guide/aria/menu)" + }, + { + "name": "see", + "comment": "[MenuBar](guide/aria/menubar)" } ], - "rawComment": "/**\n * A list of menu items.\n *\n * A `ngMenu` is used to offer a list of menu item choices to users. Menus can be nested\n * within other menus to create sub-menus. It works in conjunction with `ngMenuTrigger`\n * and `ngMenuItem` directives.\n *\n * ```html\n * \n *\n *
    \n *
    Star
    \n *
    Edit
    \n *
    More
    \n *
    \n *\n *
    \n *
    Sub Item 1
    \n *
    Sub Item 2
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * An item in a Menu.\n *\n * `ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\n * or action a user can take. They can also act as triggers for sub-menus.\n *\n * ```html\n *
    Action Item
    \n *\n *
    Submenu Trigger
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */", "implements": [], + "isStandalone": true, + "selector": "[ngMenuItem]", + "exportAs": [ + "ngMenuItem" + ], "source": { - "filePath": "/src/aria/menu/menu.ts", - "startLine": 143, - "endLine": 274 + "filePath": "/src/aria/menu/menu-item.ts", + "startLine": 33, + "endLine": 107 } }, { @@ -363,7 +455,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -376,7 +468,7 @@ }, { "name": "softDisabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -389,7 +481,7 @@ }, { "name": "textDirection", - "type": "any", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" @@ -399,7 +491,7 @@ }, { "name": "values", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "readonly", @@ -414,7 +506,7 @@ }, { "name": "wrap", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -427,7 +519,7 @@ }, { "name": "typeaheadDelay", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -440,7 +532,7 @@ }, { "name": "onSelect", - "type": "any", + "type": "OutputEmitterRef", "memberType": "property", "memberTags": [ "output" @@ -493,9 +585,17 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Menu](guide/aria/menu)" + }, + { + "name": "see", + "comment": "[MenuBar](guide/aria/menubar)" } ], - "rawComment": "/**\n * A menu bar of menu items.\n *\n * Like the `ngMenu`, a `ngMenuBar` is used to offer a list of menu item choices to users.\n * However, a menubar is used to display a persistent, top-level, always-visible set of\n * menu item choices, typically found at the top of an application window.\n *\n * ```html\n *
    \n * \n * \n *
    \n *\n *
    \n *
    New
    \n *
    Open
    \n *
    \n *\n *
    \n *
    Cut
    \n *
    Copy
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A menu bar of menu items.\n *\n * Like the `ngMenu`, a `ngMenuBar` is used to offer a list of menu item choices to users.\n * However, a menubar is used to display a persistent, top-level, always-visible set of\n * menu item choices, typically found at the top of an application window.\n *\n * ```html\n *
    \n * \n * \n *
    \n *\n *
    \n *
    New
    \n *
    Open
    \n *
    \n *\n *
    \n *
    Cut
    \n *
    Copy
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */", "implements": [], "isStandalone": true, "selector": "[ngMenuBar]", @@ -503,15 +603,15 @@ "ngMenuBar" ], "source": { - "filePath": "/src/aria/menu/menu.ts", - "startLine": 302, - "endLine": 386 + "filePath": "/src/aria/menu/menu-bar.ts", + "startLine": 56, + "endLine": 141 } }, { - "name": "MenuItem", + "name": "Menu", "isAbstract": false, - "entryType": "directive", + "entryType": "undecorated_class", "members": [ { "name": "element", @@ -524,144 +624,102 @@ "jsdocTags": [] }, { - "name": "id", - "type": "any", + "name": "textDirection", + "type": "WritableSignal", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "The unique ID of the menu item.", - "jsdocTags": [], - "inputAlias": "id", - "isRequiredInput": false + "description": "The directionality (LTR / RTL) context for the application (or a subtree of it).", + "jsdocTags": [] }, { - "name": "value", - "type": "any", + "name": "id", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "The value of the menu item.", - "jsdocTags": [], - "inputAlias": "value", - "isRequiredInput": true + "description": "The unique ID of the menu.", + "jsdocTags": [] }, { - "name": "disabled", - "type": "any", + "name": "wrap", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "Whether the menu item is disabled.", - "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false + "description": "Whether the menu should wrap its items.", + "jsdocTags": [] }, { - "name": "searchTerm", - "type": "any", + "name": "typeaheadDelay", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly", - "input", - "output" + "readonly" ], - "description": "The search term associated with the menu item.", - "jsdocTags": [], - "inputAlias": "searchTerm", - "isRequiredInput": false, - "outputAlias": "searchTermChange" + "description": "The delay in milliseconds before the typeahead buffer is cleared.", + "jsdocTags": [] }, { - "name": "parent", - "type": "any", + "name": "disabled", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly" ], - "description": "A reference to the parent menu or menubar.", + "description": "Whether the menu is disabled.", "jsdocTags": [] }, { - "name": "submenu", - "type": "any", + "name": "parent", + "type": "WritableSignal | MenuItem | undefined>", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "The submenu associated with the menu item.", - "jsdocTags": [], - "inputAlias": "submenu", - "isRequiredInput": false + "description": "A reference to the parent menu item or menu trigger.", + "jsdocTags": [] }, { - "name": "active", - "type": "any", + "name": "visible", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the menu item is active.", + "description": "Whether the menu is visible.", "jsdocTags": [] }, { - "name": "expanded", - "type": "any", + "name": "tabIndex", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the menu is expanded.", + "description": "The tab index of the menu.", "jsdocTags": [] }, { - "name": "hasPopup", - "type": "any", + "name": "onSelect", + "type": "OutputEmitterRef", "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the menu item has a popup.", + "memberTags": [], + "description": "A callback function triggered when a menu item is selected.", "jsdocTags": [] }, { - "name": "open", - "signatures": [ - { - "name": "open", - "entryType": "function", - "description": "Opens the submenu focusing on the first menu item.", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [], - "rawComment": "/** Opens the submenu focusing on the first menu item. */", - "returnType": "void" - } + "name": "expansionDelay", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly" ], - "implementation": { - "params": [], - "isNewType": false, - "returnType": "void", - "generics": [], - "name": "open", - "description": "Opens the submenu focusing on the first menu item.", - "entryType": "function", - "jsdocTags": [], - "rawComment": "/** Opens the submenu focusing on the first menu item. */" - }, - "entryType": "function", - "description": "Opens the submenu focusing on the first menu item.", - "jsdocTags": [], - "rawComment": "/** Opens the submenu focusing on the first menu item. */", - "memberType": "method", - "memberTags": [] + "description": "The delay in milliseconds before expanding sub-menus on hover.", + "jsdocTags": [] }, { "name": "close", @@ -669,12 +727,12 @@ { "name": "close", "entryType": "function", - "description": "Closes the submenu.", + "description": "Closes the menu.", "generics": [], "isNewType": false, "jsdocTags": [], "params": [], - "rawComment": "/** Closes the submenu. */", + "rawComment": "/** Closes the menu. */", "returnType": "void" } ], @@ -684,15 +742,15 @@ "returnType": "void", "generics": [], "name": "close", - "description": "Closes the submenu.", + "description": "Closes the menu.", "entryType": "function", "jsdocTags": [], - "rawComment": "/** Closes the submenu. */" + "rawComment": "/** Closes the menu. */" }, "entryType": "function", - "description": "Closes the submenu.", + "description": "Closes the menu.", "jsdocTags": [], - "rawComment": "/** Closes the submenu. */", + "rawComment": "/** Closes the menu. */", "memberType": "method", "memberTags": [] } @@ -702,51 +760,33 @@ "name": "V" } ], - "description": "An item in a Menu.\n\n`ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\nor action a user can take. They can also act as triggers for sub-menus.\n\n```html\n
    Action Item
    \n\n
    Submenu Trigger
    \n```", + "description": "A list of menu items.\n\nA `ngMenu` is used to offer a list of menu item choices to users. Menus can be nested\nwithin other menus to create sub-menus. It works in conjunction with `ngMenuTrigger`\nand `ngMenuItem` directives.\n\n```html\n\n\n
    \n
    Star
    \n
    Edit
    \n
    More
    \n
    \n\n
    \n
    Sub Item 1
    \n
    Sub Item 2
    \n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" - } - ], - "rawComment": "/**\n * An item in a Menu.\n *\n * `ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\n * or action a user can take. They can also act as triggers for sub-menus.\n *\n * ```html\n *
    Action Item
    \n *\n *
    Submenu Trigger
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], - "isStandalone": true, - "selector": "[ngMenuItem]", - "exportAs": [ - "ngMenuItem" - ], - "source": { - "filePath": "/src/aria/menu/menu.ts", - "startLine": 402, - "endLine": 482 - } - }, - { - "name": "MenuContent", - "isAbstract": false, - "entryType": "undecorated_class", - "members": [], - "generics": [], - "description": "Defers the rendering of the menu content.\n\nThis structural directive should be applied to an `ng-template` within a `ngMenu`\nor `ngMenuBar` to lazily render its content only when the menu is opened.\n\n```html\n
    \n \n
    Lazy Item 1
    \n
    Lazy Item 2
    \n
    \n
    \n```", - "jsdocTags": [ + }, { - "name": "developerPreview", - "comment": "21.0" + "name": "see", + "comment": "[Menu](guide/aria/menu)" + }, + { + "name": "see", + "comment": "[MenuBar](guide/aria/menubar)" } ], - "rawComment": "/**\n * Defers the rendering of the menu content.\n *\n * This structural directive should be applied to an `ng-template` within a `ngMenu`\n * or `ngMenuBar` to lazily render its content only when the menu is opened.\n *\n * ```html\n *
    \n * \n *
    Lazy Item 1
    \n *
    Lazy Item 2
    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A list of menu items.\n *\n * A `ngMenu` is used to offer a list of menu item choices to users. Menus can be nested\n * within other menus to create sub-menus. It works in conjunction with `ngMenuTrigger`\n * and `ngMenuItem` directives.\n *\n * ```html\n * \n *\n *
    \n *
    Star
    \n *
    Edit
    \n *
    More
    \n *
    \n *\n *
    \n *
    Sub Item 1
    \n *
    Sub Item 2
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */", "implements": [], "source": { "filePath": "/src/aria/menu/menu.ts", - "startLine": 501, - "endLine": 506 + "startLine": 58, + "endLine": 194 } } ], "symbols": [ [ - "afterRenderEffect", + "Directive", "@angular/core" ], [ @@ -757,14 +797,6 @@ "computed", "@angular/core" ], - [ - "contentChildren", - "@angular/core" - ], - [ - "Directive", - "@angular/core" - ], [ "effect", "@angular/core" @@ -782,67 +814,51 @@ "@angular/core" ], [ - "model", - "@angular/core" + "Directionality", + "@angular/cdk/bidi" ], [ - "output", + "model", "@angular/core" ], [ - "Signal", - "@angular/core" + "_IdGenerator", + "@angular/cdk/a11y" ], [ - "signal", + "afterRenderEffect", "@angular/core" ], [ - "untracked", + "contentChildren", "@angular/core" ], [ - "SignalLike", - "@angular/aria/private" - ], - [ - "MenuBarPattern", - "@angular/aria/private" - ], - [ - "MenuItemPattern", - "@angular/aria/private" - ], - [ - "MenuPattern", - "@angular/aria/private" - ], - [ - "MenuTriggerPattern", - "@angular/aria/private" + "output", + "@angular/core" ], [ - "DeferredContent", - "@angular/aria/private" + "signal", + "@angular/core" ], [ - "DeferredContentAware", - "@angular/aria/private" + "Signal", + "@angular/core" ], [ - "_IdGenerator", - "@angular/cdk/a11y" + "untracked", + "@angular/core" ], [ - "Directionality", - "@angular/cdk/bidi" + "MenuContent", + "@angular/aria/menu" ], [ "MenuTrigger", "@angular/aria/menu" ], [ - "Menu", + "MenuItem", "@angular/aria/menu" ], [ @@ -850,7 +866,7 @@ "@angular/aria/menu" ], [ - "MenuItem", + "Menu", "@angular/aria/menu" ], [ @@ -898,55 +914,55 @@ "@angular/aria/menu" ], [ - "Menu", + "MenuItem", "@angular/aria/menu" ], [ - "Menu.element", + "MenuItem.element", "@angular/aria/menu" ], [ - "Menu.textDirection", + "MenuItem.id", "@angular/aria/menu" ], [ - "Menu.id", + "MenuItem.value", "@angular/aria/menu" ], [ - "Menu.wrap", + "MenuItem.disabled", "@angular/aria/menu" ], [ - "Menu.typeaheadDelay", + "MenuItem.searchTerm", "@angular/aria/menu" ], [ - "Menu.disabled", + "MenuItem.parent", "@angular/aria/menu" ], [ - "Menu.parent", + "MenuItem.submenu", "@angular/aria/menu" ], [ - "Menu.visible", + "MenuItem.active", "@angular/aria/menu" ], [ - "Menu.tabIndex", + "MenuItem.expanded", "@angular/aria/menu" ], [ - "Menu.onSelect", + "MenuItem.hasPopup", "@angular/aria/menu" ], [ - "Menu.expansionDelay", + "MenuItem.open", "@angular/aria/menu" ], [ - "Menu.close", + "MenuItem.close", "@angular/aria/menu" ], [ @@ -990,59 +1006,55 @@ "@angular/aria/menu" ], [ - "MenuItem", - "@angular/aria/menu" - ], - [ - "MenuItem.element", + "Menu", "@angular/aria/menu" ], [ - "MenuItem.id", + "Menu.element", "@angular/aria/menu" ], [ - "MenuItem.value", + "Menu.textDirection", "@angular/aria/menu" ], [ - "MenuItem.disabled", + "Menu.id", "@angular/aria/menu" ], [ - "MenuItem.searchTerm", + "Menu.wrap", "@angular/aria/menu" ], [ - "MenuItem.parent", + "Menu.typeaheadDelay", "@angular/aria/menu" ], [ - "MenuItem.submenu", + "Menu.disabled", "@angular/aria/menu" ], [ - "MenuItem.active", + "Menu.parent", "@angular/aria/menu" ], [ - "MenuItem.expanded", + "Menu.visible", "@angular/aria/menu" ], [ - "MenuItem.hasPopup", + "Menu.tabIndex", "@angular/aria/menu" ], [ - "MenuItem.open", + "Menu.onSelect", "@angular/aria/menu" ], [ - "MenuItem.close", + "Menu.expansionDelay", "@angular/aria/menu" ], [ - "MenuContent", + "Menu.close", "@angular/aria/menu" ] ] diff --git a/adev/src/content/aria/aria-tabs.json b/adev/src/content/aria/aria-tabs.json index 12688a1af30d..496c063834ca 100755 --- a/adev/src/content/aria/aria-tabs.json +++ b/adev/src/content/aria/aria-tabs.json @@ -4,6 +4,31 @@ "moduleName": "@angular/aria/tabs", "normalizedModuleName": "angular_aria_tabs", "entries": [ + { + "name": "TabContent", + "isAbstract": false, + "entryType": "undecorated_class", + "members": [], + "generics": [], + "description": "A TabContent container for the lazy-loaded content.\n\nThis structural directive should be applied to an `ng-template` within an `ngTabPanel`.\nIt enables lazy loading of the tab's content, meaning the content is only rendered\nwhen the tab is activated for the first time.\n\n```html\n
    \n \n

    This content will be loaded when 'myTabId' is selected.

    \n
    \n
    \n```", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tabs](guide/aria/tabs)" + } + ], + "rawComment": "/**\n * A TabContent container for the lazy-loaded content.\n *\n * This structural directive should be applied to an `ng-template` within an `ngTabPanel`.\n * It enables lazy loading of the tab's content, meaning the content is only rendered\n * when the tab is activated for the first time.\n *\n * ```html\n *
    \n * \n *

    This content will be loaded when 'myTabId' is selected.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tabs](guide/aria/tabs)\n */", + "implements": [], + "source": { + "filePath": "/src/aria/tabs/tab-content.ts", + "startLine": 31, + "endLine": 36 + } + }, { "name": "Tabs", "isAbstract": false, @@ -26,9 +51,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tabs](guide/aria/tabs)" } ], - "rawComment": "/**\n * A Tabs container.\n *\n * The `ngTabs` directive represents a set of layered sections of content. It acts as the\n * overarching container for a tabbed interface, coordinating the behavior of `ngTabList`,\n * `ngTab`, and `ngTabPanel` directives.\n *\n * ```html\n *
    \n *
      \n *
    • Tab 1
    • \n *
    • Tab 2
    • \n *
    • Tab 3
    • \n *
    \n *\n *
    \n * Content for Tab 1\n *
    \n *
    \n * Content for Tab 2\n *
    \n *
    \n * Content for Tab 3\n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A Tabs container.\n *\n * The `ngTabs` directive represents a set of layered sections of content. It acts as the\n * overarching container for a tabbed interface, coordinating the behavior of `ngTabList`,\n * `ngTab`, and `ngTabPanel` directives.\n *\n * ```html\n *
    \n *
      \n *
    • Tab 1
    • \n *
    • Tab 2
    • \n *
    • Tab 3
    • \n *
    \n *\n *
    \n * Content for Tab 1\n *
    \n *
    \n * Content for Tab 2\n *
    \n *
    \n * Content for Tab 3\n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tabs](guide/aria/tabs)\n */", "implements": [], "isStandalone": true, "selector": "[ngTabs]", @@ -37,14 +66,14 @@ ], "source": { "filePath": "/src/aria/tabs/tabs.ts", - "startLine": 74, - "endLine": 120 + "startLine": 45, + "endLine": 92 } }, { - "name": "TabList", + "name": "TabPanel", "isAbstract": false, - "entryType": "directive", + "entryType": "undecorated_class", "members": [ { "name": "element", @@ -57,107 +86,34 @@ "jsdocTags": [] }, { - "name": "textDirection", - "type": "any", + "name": "id", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Text direction.", + "description": "A global unique identifier for the tab.", "jsdocTags": [] }, { - "name": "orientation", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "Whether the tablist is vertically or horizontally oriented.", - "jsdocTags": [], - "inputAlias": "orientation", - "isRequiredInput": false - }, - { - "name": "wrap", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "Whether focus should wrap when navigating.", - "jsdocTags": [], - "inputAlias": "wrap", - "isRequiredInput": false - }, - { - "name": "softDisabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", - "jsdocTags": [], - "inputAlias": "softDisabled", - "isRequiredInput": false - }, - { - "name": "focusMode", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "The focus strategy used by the tablist.\n- `roving`: Focus is moved to the active tab using `tabindex`.\n- `activedescendant`: Focus remains on the tablist container, and `aria-activedescendant` is used to indicate the active tab.", - "jsdocTags": [], - "inputAlias": "focusMode", - "isRequiredInput": false - }, - { - "name": "selectionMode", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "The selection strategy used by the tablist.\n- `follow`: The focused tab is automatically selected.\n- `explicit`: Tabs are selected explicitly by the user (e.g., via click or spacebar).", - "jsdocTags": [], - "inputAlias": "selectionMode", - "isRequiredInput": false - }, - { - "name": "selectedTab", - "type": "any", + "name": "value", + "type": "InputSignal", "memberType": "property", "memberTags": [ - "readonly", - "input", - "output" + "readonly" ], - "description": "The current selected tab.", - "jsdocTags": [], - "inputAlias": "selectedTab", - "isRequiredInput": false, - "outputAlias": "selectedTabChange" + "description": "A local unique identifier for the tabpanel.", + "jsdocTags": [] }, { - "name": "disabled", - "type": "any", + "name": "visible", + "type": "Signal", "memberType": "property", "memberTags": [ - "readonly", - "input" + "readonly" ], - "description": "Whether the tablist is disabled.", - "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false + "description": "Whether the tab panel is visible.", + "jsdocTags": [] }, { "name": "ngOnInit", @@ -224,79 +180,29 @@ "rawComment": "", "memberType": "method", "memberTags": [] - }, - { - "name": "open", - "signatures": [ - { - "name": "open", - "entryType": "function", - "description": "Opens the tab panel with the specified value.", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [ - { - "name": "value", - "description": "", - "type": "string", - "isOptional": false, - "isRestParam": false - } - ], - "rawComment": "/** Opens the tab panel with the specified value. */", - "returnType": "boolean" - } - ], - "implementation": { - "params": [ - { - "name": "value", - "description": "", - "type": "string", - "isOptional": false, - "isRestParam": false - } - ], - "isNewType": false, - "returnType": "boolean", - "generics": [], - "name": "open", - "description": "Opens the tab panel with the specified value.", - "entryType": "function", - "jsdocTags": [], - "rawComment": "/** Opens the tab panel with the specified value. */" - }, - "entryType": "function", - "description": "Opens the tab panel with the specified value.", - "jsdocTags": [], - "rawComment": "/** Opens the tab panel with the specified value. */", - "memberType": "method", - "memberTags": [] } ], "generics": [], - "description": "A TabList container.\n\nThe `ngTabList` directive controls a list of `ngTab` elements. It manages keyboard\nnavigation, selection, and the overall orientation of the tabs. It should be placed\nwithin an `ngTabs` container.\n\n```html\n
      \n
    • First Tab
    • \n
    • Second Tab
    • \n
    \n```", + "description": "A TabPanel container for the resources of layered content associated with a tab.\n\nThe `ngTabPanel` directive holds the content for a specific tab. It is linked to an\n`ngTab` by a matching `value`. If a tab panel is hidden, the `inert` attribute will be\napplied to remove it from the accessibility tree. Proper styling is required for visual hiding.\n\n```html\n
    \n \n \n \n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tabs](guide/aria/tabs)" } ], - "rawComment": "/**\n * A TabList container.\n *\n * The `ngTabList` directive controls a list of `ngTab` elements. It manages keyboard\n * navigation, selection, and the overall orientation of the tabs. It should be placed\n * within an `ngTabs` container.\n *\n * ```html\n *
      \n *
    • First Tab
    • \n *
    • Second Tab
    • \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A TabPanel container for the resources of layered content associated with a tab.\n *\n * The `ngTabPanel` directive holds the content for a specific tab. It is linked to an\n * `ngTab` by a matching `value`. If a tab panel is hidden, the `inert` attribute will be\n * applied to remove it from the accessibility tree. Proper styling is required for visual hiding.\n *\n * ```html\n *
    \n * \n * \n * \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tabs](guide/aria/tabs)\n */", "implements": [ "OnInit", "OnDestroy" ], - "isStandalone": true, - "selector": "[ngTabList]", - "exportAs": [ - "ngTabList" - ], "source": { - "filePath": "/src/aria/tabs/tabs.ts", - "startLine": 138, - "endLine": 264 + "filePath": "/src/aria/tabs/tab-panel.ts", + "startLine": 42, + "endLine": 103 } }, { @@ -316,7 +222,7 @@ }, { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -329,7 +235,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -342,7 +248,7 @@ }, { "name": "value", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -355,7 +261,7 @@ }, { "name": "active", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -365,7 +271,7 @@ }, { "name": "selected", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -479,9 +385,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tabs](guide/aria/tabs)" } ], - "rawComment": "/**\n * A selectable tab in a TabList.\n *\n * The `ngTab` directive represents an individual tab control within an `ngTabList`. It\n * requires a `value` that uniquely identifies it and links it to a corresponding `ngTabPanel`.\n *\n * ```html\n *
  • \n * My Tab Label\n *
  • \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A selectable tab in a TabList.\n *\n * The `ngTab` directive represents an individual tab control within an `ngTabList`. It\n * requires a `value` that uniquely identifies it and links it to a corresponding `ngTabPanel`.\n *\n * ```html\n *
  • \n * My Tab Label\n *
  • \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tabs](guide/aria/tabs)\n */", "implements": [ "HasElement", "OnInit", @@ -493,15 +403,15 @@ "ngTab" ], "source": { - "filePath": "/src/aria/tabs/tabs.ts", - "startLine": 280, - "endLine": 350 + "filePath": "/src/aria/tabs/tab.ts", + "startLine": 41, + "endLine": 111 } }, { - "name": "TabPanel", + "name": "TabList", "isAbstract": false, - "entryType": "undecorated_class", + "entryType": "directive", "members": [ { "name": "element", @@ -514,34 +424,107 @@ "jsdocTags": [] }, { - "name": "id", - "type": "any", + "name": "textDirection", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "A global unique identifier for the tab.", + "description": "Text direction.", "jsdocTags": [] }, { - "name": "value", - "type": "any", + "name": "orientation", + "type": "InputSignal<\"vertical\" | \"horizontal\">", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "A local unique identifier for the tabpanel.", - "jsdocTags": [] + "description": "Whether the tablist is vertically or horizontally oriented.", + "jsdocTags": [], + "inputAlias": "orientation", + "isRequiredInput": false }, { - "name": "visible", - "type": "any", + "name": "wrap", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ - "readonly" + "readonly", + "input" ], - "description": "Whether the tab panel is visible.", - "jsdocTags": [] + "description": "Whether focus should wrap when navigating.", + "jsdocTags": [], + "inputAlias": "wrap", + "isRequiredInput": false + }, + { + "name": "softDisabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", + "jsdocTags": [], + "inputAlias": "softDisabled", + "isRequiredInput": false + }, + { + "name": "focusMode", + "type": "InputSignal<\"roving\" | \"activedescendant\">", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The focus strategy used by the tablist.\n- `roving`: Focus is moved to the active tab using `tabindex`.\n- `activedescendant`: Focus remains on the tablist container, and `aria-activedescendant` is used to indicate the active tab.", + "jsdocTags": [], + "inputAlias": "focusMode", + "isRequiredInput": false + }, + { + "name": "selectionMode", + "type": "InputSignal<\"follow\" | \"explicit\">", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "The selection strategy used by the tablist.\n- `follow`: The focused tab is automatically selected.\n- `explicit`: Tabs are selected explicitly by the user (e.g., via click or spacebar).", + "jsdocTags": [], + "inputAlias": "selectionMode", + "isRequiredInput": false + }, + { + "name": "selectedTab", + "type": "ModelSignal", + "memberType": "property", + "memberTags": [ + "readonly", + "input", + "output" + ], + "description": "The current selected tab.", + "jsdocTags": [], + "inputAlias": "selectedTab", + "isRequiredInput": false, + "outputAlias": "selectedTabChange" + }, + { + "name": "disabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "Whether the tablist is disabled.", + "jsdocTags": [], + "inputAlias": "disabled", + "isRequiredInput": false }, { "name": "ngOnInit", @@ -608,70 +591,95 @@ "rawComment": "", "memberType": "method", "memberTags": [] + }, + { + "name": "open", + "signatures": [ + { + "name": "open", + "entryType": "function", + "description": "Opens the tab panel with the specified value.", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [ + { + "name": "value", + "description": "", + "type": "string", + "isOptional": false, + "isRestParam": false + } + ], + "rawComment": "/** Opens the tab panel with the specified value. */", + "returnType": "boolean" + } + ], + "implementation": { + "params": [ + { + "name": "value", + "description": "", + "type": "string", + "isOptional": false, + "isRestParam": false + } + ], + "isNewType": false, + "returnType": "boolean", + "generics": [], + "name": "open", + "description": "Opens the tab panel with the specified value.", + "entryType": "function", + "jsdocTags": [], + "rawComment": "/** Opens the tab panel with the specified value. */" + }, + "entryType": "function", + "description": "Opens the tab panel with the specified value.", + "jsdocTags": [], + "rawComment": "/** Opens the tab panel with the specified value. */", + "memberType": "method", + "memberTags": [] } ], "generics": [], - "description": "A TabPanel container for the resources of layered content associated with a tab.\n\nThe `ngTabPanel` directive holds the content for a specific tab. It is linked to an\n`ngTab` by a matching `value`. If a tab panel is hidden, the `inert` attribute will be\napplied to remove it from the accessibility tree. Proper styling is required for visual hiding.\n\n```html\n
    \n \n \n \n
    \n```", + "description": "A TabList container.\n\nThe `ngTabList` directive controls a list of `ngTab` elements. It manages keyboard\nnavigation, selection, and the overall orientation of the tabs. It should be placed\nwithin an `ngTabs` container.\n\n```html\n
      \n
    • First Tab
    • \n
    • Second Tab
    • \n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tabs](guide/aria/tabs)" } ], - "rawComment": "/**\n * A TabPanel container for the resources of layered content associated with a tab.\n *\n * The `ngTabPanel` directive holds the content for a specific tab. It is linked to an\n * `ngTab` by a matching `value`. If a tab panel is hidden, the `inert` attribute will be\n * applied to remove it from the accessibility tree. Proper styling is required for visual hiding.\n *\n * ```html\n *
    \n * \n * \n * \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A TabList container.\n *\n * The `ngTabList` directive controls a list of `ngTab` elements. It manages keyboard\n * navigation, selection, and the overall orientation of the tabs. It should be placed\n * within an `ngTabs` container.\n *\n * ```html\n *
      \n *
    • First Tab
    • \n *
    • Second Tab
    • \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tabs](guide/aria/tabs)\n */", "implements": [ "OnInit", "OnDestroy" ], - "source": { - "filePath": "/src/aria/tabs/tabs.ts", - "startLine": 369, - "endLine": 430 - } - }, - { - "name": "TabContent", - "isAbstract": false, - "entryType": "undecorated_class", - "members": [], - "generics": [], - "description": "A TabContent container for the lazy-loaded content.\n\nThis structural directive should be applied to an `ng-template` within an `ngTabPanel`.\nIt enables lazy loading of the tab's content, meaning the content is only rendered\nwhen the tab is activated for the first time.\n\n```html\n
    \n \n

    This content will be loaded when 'myTabId' is selected.

    \n
    \n
    \n```", - "jsdocTags": [ - { - "name": "developerPreview", - "comment": "21.0" - } + "isStandalone": true, + "selector": "[ngTabList]", + "exportAs": [ + "ngTabList" ], - "rawComment": "/**\n * A TabContent container for the lazy-loaded content.\n *\n * This structural directive should be applied to an `ng-template` within an `ngTabPanel`.\n * It enables lazy loading of the tab's content, meaning the content is only rendered\n * when the tab is activated for the first time.\n *\n * ```html\n *
    \n * \n *

    This content will be loaded when 'myTabId' is selected.

    \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], "source": { - "filePath": "/src/aria/tabs/tabs.ts", - "startLine": 449, - "endLine": 454 + "filePath": "/src/aria/tabs/tab-list.ts", + "startLine": 45, + "endLine": 174 } } ], "symbols": [ [ - "_IdGenerator", - "@angular/cdk/a11y" - ], - [ - "Directionality", - "@angular/cdk/bidi" - ], - [ - "booleanAttribute", + "Directive", "@angular/core" ], [ "computed", "@angular/core" ], - [ - "Directive", - "@angular/core" - ], [ "ElementRef", "@angular/core" @@ -681,15 +689,15 @@ "@angular/core" ], [ - "input", + "signal", "@angular/core" ], [ - "model", - "@angular/core" + "_IdGenerator", + "@angular/cdk/a11y" ], [ - "signal", + "input", "@angular/core" ], [ @@ -705,31 +713,27 @@ "@angular/core" ], [ - "TabListPattern", - "@angular/aria/private" - ], - [ - "TabPanelPattern", - "@angular/aria/private" + "booleanAttribute", + "@angular/core" ], [ - "TabPattern", - "@angular/aria/private" + "Directionality", + "@angular/cdk/bidi" ], [ - "DeferredContent", - "@angular/aria/private" + "model", + "@angular/core" ], [ - "DeferredContentAware", - "@angular/aria/private" + "TabContent", + "@angular/aria/tabs" ], [ "Tabs", "@angular/aria/tabs" ], [ - "TabList", + "TabPanel", "@angular/aria/tabs" ], [ @@ -737,7 +741,7 @@ "@angular/aria/tabs" ], [ - "TabPanel", + "TabList", "@angular/aria/tabs" ], [ @@ -753,127 +757,123 @@ "@angular/aria/tabs" ], [ - "TabList", - "@angular/aria/tabs" - ], - [ - "TabList.element", + "TabPanel", "@angular/aria/tabs" ], [ - "TabList.textDirection", + "TabPanel.element", "@angular/aria/tabs" ], [ - "TabList.orientation", + "TabPanel.id", "@angular/aria/tabs" ], [ - "TabList.wrap", + "TabPanel.value", "@angular/aria/tabs" ], [ - "TabList.softDisabled", + "TabPanel.visible", "@angular/aria/tabs" ], [ - "TabList.focusMode", + "TabPanel.ngOnInit", "@angular/aria/tabs" ], [ - "TabList.selectionMode", + "TabPanel.ngOnDestroy", "@angular/aria/tabs" ], [ - "TabList.selectedTab", + "Tab", "@angular/aria/tabs" ], [ - "TabList.disabled", + "Tab.element", "@angular/aria/tabs" ], [ - "TabList.ngOnInit", + "Tab.id", "@angular/aria/tabs" ], [ - "TabList.ngOnDestroy", + "Tab.disabled", "@angular/aria/tabs" ], [ - "TabList.open", + "Tab.value", "@angular/aria/tabs" ], [ - "Tab", + "Tab.active", "@angular/aria/tabs" ], [ - "Tab.element", + "Tab.selected", "@angular/aria/tabs" ], [ - "Tab.id", + "Tab.open", "@angular/aria/tabs" ], [ - "Tab.disabled", + "Tab.ngOnInit", "@angular/aria/tabs" ], [ - "Tab.value", + "Tab.ngOnDestroy", "@angular/aria/tabs" ], [ - "Tab.active", + "TabList", "@angular/aria/tabs" ], [ - "Tab.selected", + "TabList.element", "@angular/aria/tabs" ], [ - "Tab.open", + "TabList.textDirection", "@angular/aria/tabs" ], [ - "Tab.ngOnInit", + "TabList.orientation", "@angular/aria/tabs" ], [ - "Tab.ngOnDestroy", + "TabList.wrap", "@angular/aria/tabs" ], [ - "TabPanel", + "TabList.softDisabled", "@angular/aria/tabs" ], [ - "TabPanel.element", + "TabList.focusMode", "@angular/aria/tabs" ], [ - "TabPanel.id", + "TabList.selectionMode", "@angular/aria/tabs" ], [ - "TabPanel.value", + "TabList.selectedTab", "@angular/aria/tabs" ], [ - "TabPanel.visible", + "TabList.disabled", "@angular/aria/tabs" ], [ - "TabPanel.ngOnInit", + "TabList.ngOnInit", "@angular/aria/tabs" ], [ - "TabPanel.ngOnDestroy", + "TabList.ngOnDestroy", "@angular/aria/tabs" ], [ - "TabContent", + "TabList.open", "@angular/aria/tabs" ] ] diff --git a/adev/src/content/aria/aria-toolbar.json b/adev/src/content/aria/aria-toolbar.json index 5b4c9c62b58e..f54e57196714 100755 --- a/adev/src/content/aria/aria-toolbar.json +++ b/adev/src/content/aria/aria-toolbar.json @@ -4,6 +4,77 @@ "moduleName": "@angular/aria/toolbar", "normalizedModuleName": "angular_aria_toolbar", "entries": [ + { + "name": "ToolbarWidgetGroup", + "isAbstract": false, + "entryType": "directive", + "members": [ + { + "name": "element", + "type": "HTMLElement", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "A reference to the host element.", + "jsdocTags": [] + }, + { + "name": "disabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "Whether the widget group is disabled.", + "jsdocTags": [], + "inputAlias": "disabled", + "isRequiredInput": false + }, + { + "name": "multi", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly", + "input" + ], + "description": "Whether the group allows multiple widgets to be selected.", + "jsdocTags": [], + "inputAlias": "multi", + "isRequiredInput": false + } + ], + "generics": [ + { + "name": "V" + } + ], + "description": "A directive that groups toolbar widgets, used for more complex widgets like radio groups\nthat have their own internal navigation.", + "jsdocTags": [ + { + "name": "developerPreview", + "comment": "21.0" + }, + { + "name": "see", + "comment": "[Toolbar](guide/aria/toolbar)" + } + ], + "rawComment": "/**\n * A directive that groups toolbar widgets, used for more complex widgets like radio groups\n * that have their own internal navigation.\n *\n * @developerPreview 21.0\n *\n * @see [Toolbar](guide/aria/toolbar)\n */", + "implements": [], + "isStandalone": true, + "selector": "[ngToolbarWidgetGroup]", + "exportAs": [ + "ngToolbarWidgetGroup" + ], + "source": { + "filePath": "/src/aria/toolbar/toolbar-widget-group.ts", + "startLine": 31, + "endLine": 67 + } + }, { "name": "Toolbar", "isAbstract": false, @@ -21,7 +92,7 @@ }, { "name": "textDirection", - "type": "any", + "type": "WritableSignal", "memberType": "property", "memberTags": [ "readonly" @@ -31,7 +102,7 @@ }, { "name": "orientation", - "type": "any", + "type": "InputSignal<\"vertical\" | \"horizontal\">", "memberType": "property", "memberTags": [ "readonly", @@ -44,7 +115,7 @@ }, { "name": "softDisabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "input" @@ -56,7 +127,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -69,7 +140,7 @@ }, { "name": "wrap", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -82,7 +153,7 @@ }, { "name": "values", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "readonly", @@ -106,9 +177,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Toolbar](guide/aria/toolbar)" } ], - "rawComment": "/**\n * A toolbar widget container for a group of interactive widgets, such as\n * buttons or radio groups. It provides a single point of reference for keyboard navigation\n * and focus management. It supports various orientations and disabled states.\n *\n * ```html\n *
    \n * \n * \n *\n *
    \n * \n * \n * \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A toolbar widget container for a group of interactive widgets, such as\n * buttons or radio groups. It provides a single point of reference for keyboard navigation\n * and focus management. It supports various orientations and disabled states.\n *\n * ```html\n *
    \n * \n * \n *\n *
    \n * \n * \n * \n *
    \n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Toolbar](guide/aria/toolbar)\n */", "implements": [], "isStandalone": true, "selector": "[ngToolbar]", @@ -117,8 +192,8 @@ ], "source": { "filePath": "/src/aria/toolbar/toolbar.ts", - "startLine": 65, - "endLine": 169 + "startLine": 47, + "endLine": 151 } }, { @@ -138,7 +213,7 @@ }, { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -151,7 +226,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -164,7 +239,7 @@ }, { "name": "hardDisabled", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -174,7 +249,7 @@ }, { "name": "value", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -187,7 +262,7 @@ }, { "name": "active", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -282,9 +357,13 @@ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Toolbar](guide/aria/toolbar)" } ], - "rawComment": "/**\n * A widget within a toolbar.\n *\n * The `ngToolbarWidget` directive should be applied to any native HTML element that acts\n * as an interactive widget within an `ngToolbar` or `ngToolbarWidgetGroup`. It enables\n * keyboard navigation and selection within the toolbar.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n */", + "rawComment": "/**\n * A widget within a toolbar.\n *\n * The `ngToolbarWidget` directive should be applied to any native HTML element that acts\n * as an interactive widget within an `ngToolbar` or `ngToolbarWidgetGroup`. It enables\n * keyboard navigation and selection within the toolbar.\n *\n * ```html\n * \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Toolbar](guide/aria/toolbar)\n */", "implements": [ "OnInit", "OnDestroy" @@ -295,84 +374,13 @@ "ngToolbarWidget" ], "source": { - "filePath": "/src/aria/toolbar/toolbar.ts", - "startLine": 186, - "endLine": 253 - } - }, - { - "name": "ToolbarWidgetGroup", - "isAbstract": false, - "entryType": "directive", - "members": [ - { - "name": "element", - "type": "HTMLElement", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "A reference to the host element.", - "jsdocTags": [] - }, - { - "name": "disabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "Whether the widget group is disabled.", - "jsdocTags": [], - "inputAlias": "disabled", - "isRequiredInput": false - }, - { - "name": "multi", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly", - "input" - ], - "description": "Whether the group allows multiple widgets to be selected.", - "jsdocTags": [], - "inputAlias": "multi", - "isRequiredInput": false - } - ], - "generics": [ - { - "name": "V" - } - ], - "description": "A directive that groups toolbar widgets, used for more complex widgets like radio groups\nthat have their own internal navigation.", - "jsdocTags": [ - { - "name": "developerPreview", - "comment": "21.0" - } - ], - "rawComment": "/**\n * A directive that groups toolbar widgets, used for more complex widgets like radio groups\n * that have their own internal navigation.\n *\n * @developerPreview 21.0\n */", - "implements": [], - "isStandalone": true, - "selector": "[ngToolbarWidgetGroup]", - "exportAs": [ - "ngToolbarWidgetGroup" - ], - "source": { - "filePath": "/src/aria/toolbar/toolbar.ts", - "startLine": 261, - "endLine": 296 + "filePath": "/src/aria/toolbar/toolbar-widget.ts", + "startLine": 42, + "endLine": 109 } } ], "symbols": [ - [ - "afterRenderEffect", - "@angular/core" - ], [ "Directive", "@angular/core" @@ -398,59 +406,63 @@ "@angular/core" ], [ - "signal", + "contentChildren", "@angular/core" ], [ - "OnInit", + "afterRenderEffect", "@angular/core" ], [ - "OnDestroy", + "signal", "@angular/core" ], [ - "contentChildren", + "model", "@angular/core" ], [ - "model", + "Directionality", + "@angular/cdk/bidi" + ], + [ + "OnInit", "@angular/core" ], [ - "ToolbarPattern", - "@angular/aria/private" + "OnDestroy", + "@angular/core" ], [ - "ToolbarWidgetPattern", - "@angular/aria/private" + "_IdGenerator", + "@angular/cdk/a11y" ], [ - "ToolbarWidgetGroupPattern", - "@angular/aria/private" + "ToolbarWidgetGroup", + "@angular/aria/toolbar" ], [ - "SignalLike", - "@angular/aria/private" + "Toolbar", + "@angular/aria/toolbar" ], [ - "Directionality", - "@angular/cdk/bidi" + "ToolbarWidget", + "@angular/aria/toolbar" ], [ - "_IdGenerator", - "@angular/cdk/a11y" + "ToolbarWidgetGroup", + "@angular/aria/toolbar" ], [ - "Toolbar", + "ToolbarWidgetGroup.element", "@angular/aria/toolbar" ], [ - "ToolbarWidget", + "ToolbarWidgetGroup.disabled", "@angular/aria/toolbar" ], [ - "ToolbarWidgetGroup", + "ToolbarWidgetGroup.multi", "@angular/aria/toolbar" ], [ @@ -524,22 +536,6 @@ [ "ToolbarWidget.ngOnDestroy", "@angular/aria/toolbar" - ], - [ - "ToolbarWidgetGroup", - "@angular/aria/toolbar" - ], - [ - "ToolbarWidgetGroup.element", - "@angular/aria/toolbar" - ], - [ - "ToolbarWidgetGroup.disabled", - "@angular/aria/toolbar" - ], - [ - "ToolbarWidgetGroup.multi", - "@angular/aria/toolbar" ] ] } \ No newline at end of file diff --git a/adev/src/content/aria/aria-tree.json b/adev/src/content/aria/aria-tree.json index ab7a9ac6f7e5..0809a1c4acba 100755 --- a/adev/src/content/aria/aria-tree.json +++ b/adev/src/content/aria/aria-tree.json @@ -5,7 +5,7 @@ "normalizedModuleName": "angular_aria_tree", "entries": [ { - "name": "Tree", + "name": "TreeItemGroup", "isAbstract": false, "entryType": "undecorated_class", "members": [ @@ -20,172 +20,69 @@ "jsdocTags": [] }, { - "name": "id", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "A unique identifier for the tree.", - "jsdocTags": [] - }, - { - "name": "orientation", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Orientation of the tree.", - "jsdocTags": [] - }, - { - "name": "multi", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether multi-selection is allowed.", - "jsdocTags": [] - }, - { - "name": "disabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether the tree is disabled.", - "jsdocTags": [] - }, - { - "name": "selectionMode", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The selection strategy used by the tree.\n- `explicit`: Items are selected explicitly by the user (e.g., via click or spacebar).\n- `follow`: The focused item is automatically selected.", - "jsdocTags": [] - }, - { - "name": "focusMode", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The focus strategy used by the tree.\n- `roving`: Focus is moved to the active item using `tabindex`.\n- `activedescendant`: Focus remains on the tree container, and `aria-activedescendant` is used to indicate the active item.", - "jsdocTags": [] - }, - { - "name": "wrap", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether navigation wraps.", - "jsdocTags": [] - }, - { - "name": "softDisabled", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", - "jsdocTags": [] - }, - { - "name": "typeaheadDelay", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The delay in seconds before the typeahead search is reset.", - "jsdocTags": [] - }, - { - "name": "values", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "The values of the currently selected items.", - "jsdocTags": [] - }, - { - "name": "textDirection", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" - ], - "description": "Text direction.", - "jsdocTags": [] - }, - { - "name": "nav", - "type": "any", + "name": "ownedBy", + "type": "InputSignal>", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Whether the tree is in navigation mode.", + "description": "Tree item that owns the group.", "jsdocTags": [] }, { - "name": "currentType", - "type": "any", - "memberType": "property", - "memberTags": [ - "readonly" + "name": "ngOnInit", + "signatures": [ + { + "name": "ngOnInit", + "entryType": "function", + "description": "", + "generics": [], + "isNewType": false, + "jsdocTags": [], + "params": [], + "rawComment": "", + "returnType": "void" + } ], - "description": "The `aria-current` type. It can be used in navigation trees to indicate the currently active item.\nSee https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-current for more details.", - "jsdocTags": [] + "implementation": { + "params": [], + "isNewType": false, + "returnType": "void", + "generics": [], + "name": "ngOnInit", + "description": "", + "entryType": "function", + "jsdocTags": [], + "rawComment": "" + }, + "entryType": "function", + "description": "", + "jsdocTags": [], + "rawComment": "", + "memberType": "method", + "memberTags": [] }, { - "name": "scrollActiveItemIntoView", + "name": "ngOnDestroy", "signatures": [ { - "name": "scrollActiveItemIntoView", + "name": "ngOnDestroy", "entryType": "function", "description": "", "generics": [], "isNewType": false, "jsdocTags": [], - "params": [ - { - "name": "options", - "description": "", - "type": "ScrollIntoViewOptions", - "isOptional": true, - "isRestParam": false - } - ], + "params": [], "rawComment": "", "returnType": "void" } ], "implementation": { - "params": [ - { - "name": "options", - "description": "", - "type": "ScrollIntoViewOptions", - "isOptional": true, - "isRestParam": false - } - ], + "params": [], "isNewType": false, "returnType": "void", "generics": [], - "name": "scrollActiveItemIntoView", + "name": "ngOnDestroy", "description": "", "entryType": "function", "jsdocTags": [], @@ -204,27 +101,26 @@ "name": "V" } ], - "description": "A container that transforms nested lists into an accessible, ARIA-compliant tree structure.\nIt manages the overall state of the tree, including selection, expansion, and keyboard\nnavigation.\n\n```html\n
      \n \n
    \n\n", + "description": "Group that contains children tree items.\n\nThe `ngTreeItemGroup` structural directive should be applied to an `ng-template` that\nwraps the child `ngTreeItem` elements. It is used to define a group of children for an\nexpandable `ngTreeItem`. The `ownedBy` input links the group to its parent `ngTreeItem`.\n\n```html\n
  • \n Parent Item\n
      \n \n
    • Child Item
    • \n
      \n
    \n
  • \n```", "jsdocTags": [ - { - "name": "for", - "comment": "(node of nodes; track node.name) {\n
  • \n{{ node.name }}" - }, - { - "name": "if", - "comment": "(node.children) {\n
      \n \n \n \n
    \n}\n
  • \n}\n
    \n```" - }, { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tree](guide/aria/tree)" } ], - "rawComment": "/**\n * A container that transforms nested lists into an accessible, ARIA-compliant tree structure.\n * It manages the overall state of the tree, including selection, expansion, and keyboard\n * navigation.\n *\n * ```html\n *
      \n * \n *
    \n *\n * \n * @for (node of nodes; track node.name) {\n *
  • \n * {{ node.name }}\n * @if (node.children) {\n *
      \n * \n * \n * \n *
    \n * }\n *
  • \n * }\n *
    \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [], + "rawComment": "/**\n * Group that contains children tree items.\n *\n * The `ngTreeItemGroup` structural directive should be applied to an `ng-template` that\n * wraps the child `ngTreeItem` elements. It is used to define a group of children for an\n * expandable `ngTreeItem`. The `ownedBy` input links the group to its parent `ngTreeItem`.\n *\n * ```html\n *
  • \n * Parent Item\n *
      \n * \n *
    • Child Item
    • \n *
      \n *
    \n *
  • \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tree](guide/aria/tree)\n */", + "implements": [ + "OnInit", + "OnDestroy" + ], "source": { - "filePath": "/src/aria/tree/tree.ts", - "startLine": 83, - "endLine": 240 + "filePath": "/src/aria/tree/tree-item-group.ts", + "startLine": 45, + "endLine": 89 } }, { @@ -244,7 +140,7 @@ }, { "name": "id", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -257,7 +153,7 @@ }, { "name": "value", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -270,7 +166,7 @@ }, { "name": "parent", - "type": "any", + "type": "InputSignal | TreeItemGroup>", "memberType": "property", "memberTags": [ "readonly", @@ -283,7 +179,7 @@ }, { "name": "disabled", - "type": "any", + "type": "InputSignalWithTransform", "memberType": "property", "memberTags": [ "readonly", @@ -296,7 +192,7 @@ }, { "name": "selectable", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -309,7 +205,7 @@ }, { "name": "expanded", - "type": "any", + "type": "ModelSignal", "memberType": "property", "memberTags": [ "readonly", @@ -324,7 +220,7 @@ }, { "name": "label", - "type": "any", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly", @@ -337,7 +233,7 @@ }, { "name": "searchTerm", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -357,7 +253,7 @@ }, { "name": "active", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -367,7 +263,7 @@ }, { "name": "level", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -377,7 +273,7 @@ }, { "name": "selected", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -387,7 +283,7 @@ }, { "name": "visible", - "type": "any", + "type": "Signal", "memberType": "property", "memberTags": [ "readonly" @@ -487,13 +383,13 @@ "ngTreeItem" ], "source": { - "filePath": "/src/aria/tree/tree.ts", - "startLine": 257, - "endLine": 385 + "filePath": "/src/aria/tree/tree-item.ts", + "startLine": 45, + "endLine": 173 } }, { - "name": "TreeItemGroup", + "name": "Tree", "isAbstract": false, "entryType": "undecorated_class", "members": [ @@ -508,69 +404,172 @@ "jsdocTags": [] }, { - "name": "ownedBy", - "type": "any", + "name": "id", + "type": "InputSignal", "memberType": "property", "memberTags": [ "readonly" ], - "description": "Tree item that owns the group.", + "description": "A unique identifier for the tree.", "jsdocTags": [] }, { - "name": "ngOnInit", - "signatures": [ - { - "name": "ngOnInit", - "entryType": "function", - "description": "", - "generics": [], - "isNewType": false, - "jsdocTags": [], - "params": [], - "rawComment": "", - "returnType": "void" - } + "name": "orientation", + "type": "InputSignal<\"vertical\" | \"horizontal\">", + "memberType": "property", + "memberTags": [ + "readonly" ], - "implementation": { - "params": [], - "isNewType": false, - "returnType": "void", - "generics": [], - "name": "ngOnInit", - "description": "", - "entryType": "function", - "jsdocTags": [], - "rawComment": "" - }, - "entryType": "function", - "description": "", - "jsdocTags": [], - "rawComment": "", - "memberType": "method", - "memberTags": [] + "description": "Orientation of the tree.", + "jsdocTags": [] }, { - "name": "ngOnDestroy", + "name": "multi", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether multi-selection is allowed.", + "jsdocTags": [] + }, + { + "name": "disabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the tree is disabled.", + "jsdocTags": [] + }, + { + "name": "selectionMode", + "type": "InputSignal<\"explicit\" | \"follow\">", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The selection strategy used by the tree.\n- `explicit`: Items are selected explicitly by the user (e.g., via click or spacebar).\n- `follow`: The focused item is automatically selected.", + "jsdocTags": [] + }, + { + "name": "focusMode", + "type": "InputSignal<\"roving\" | \"activedescendant\">", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The focus strategy used by the tree.\n- `roving`: Focus is moved to the active item using `tabindex`.\n- `activedescendant`: Focus remains on the tree container, and `aria-activedescendant` is used to indicate the active item.", + "jsdocTags": [] + }, + { + "name": "wrap", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether navigation wraps.", + "jsdocTags": [] + }, + { + "name": "softDisabled", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether to allow disabled items to receive focus. When `true`, disabled items are\nfocusable but not interactive. When `false`, disabled items are skipped during navigation.", + "jsdocTags": [] + }, + { + "name": "typeaheadDelay", + "type": "InputSignal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The delay in seconds before the typeahead search is reset.", + "jsdocTags": [] + }, + { + "name": "values", + "type": "ModelSignal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The values of the currently selected items.", + "jsdocTags": [] + }, + { + "name": "textDirection", + "type": "WritableSignal", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Text direction.", + "jsdocTags": [] + }, + { + "name": "nav", + "type": "InputSignalWithTransform", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "Whether the tree is in navigation mode.", + "jsdocTags": [] + }, + { + "name": "currentType", + "type": "InputSignal<\"page\" | \"step\" | \"location\" | \"date\" | \"time\" | \"true\" | \"false\">", + "memberType": "property", + "memberTags": [ + "readonly" + ], + "description": "The `aria-current` type. It can be used in navigation trees to indicate the currently active item.\nSee https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-current for more details.", + "jsdocTags": [] + }, + { + "name": "scrollActiveItemIntoView", "signatures": [ { - "name": "ngOnDestroy", + "name": "scrollActiveItemIntoView", "entryType": "function", "description": "", "generics": [], "isNewType": false, "jsdocTags": [], - "params": [], + "params": [ + { + "name": "options", + "description": "", + "type": "ScrollIntoViewOptions", + "isOptional": true, + "isRestParam": false + } + ], "rawComment": "", "returnType": "void" } ], "implementation": { - "params": [], + "params": [ + { + "name": "options", + "description": "", + "type": "ScrollIntoViewOptions", + "isOptional": true, + "isRestParam": false + } + ], "isNewType": false, "returnType": "void", "generics": [], - "name": "ngOnDestroy", + "name": "scrollActiveItemIntoView", "description": "", "entryType": "function", "jsdocTags": [], @@ -589,22 +588,23 @@ "name": "V" } ], - "description": "Group that contains children tree items.\n\nThe `ngTreeItemGroup` structural directive should be applied to an `ng-template` that\nwraps the child `ngTreeItem` elements. It is used to define a group of children for an\nexpandable `ngTreeItem`. The `ownedBy` input links the group to its parent `ngTreeItem`.\n\n```html\n
  • \n Parent Item\n
      \n \n
    • Child Item
    • \n
      \n
    \n
  • \n```", + "description": "A container that transforms nested lists into an accessible, ARIA-compliant tree structure.\nIt manages the overall state of the tree, including selection, expansion, and keyboard\nnavigation.\n\n```html\n
      \n \n
    \n\n\n @for (node of nodes; track node.name) {\n
  • \n {{ node.name }}\n @if (node.children) {\n
      \n \n \n \n
    \n }\n
  • \n }\n
    \n```", "jsdocTags": [ { "name": "developerPreview", "comment": "21.0" + }, + { + "name": "see", + "comment": "[Tree](guide/aria/tree)" } ], - "rawComment": "/**\n * Group that contains children tree items.\n *\n * The `ngTreeItemGroup` structural directive should be applied to an `ng-template` that\n * wraps the child `ngTreeItem` elements. It is used to define a group of children for an\n * expandable `ngTreeItem`. The `ownedBy` input links the group to its parent `ngTreeItem`.\n *\n * ```html\n *
  • \n * Parent Item\n *
      \n * \n *
    • Child Item
    • \n *
      \n *
    \n *
  • \n * ```\n *\n * @developerPreview 21.0\n */", - "implements": [ - "OnInit", - "OnDestroy" - ], + "rawComment": "/**\n * A container that transforms nested lists into an accessible, ARIA-compliant tree structure.\n * It manages the overall state of the tree, including selection, expansion, and keyboard\n * navigation.\n *\n * ```html\n *
      \n * \n *
    \n *\n * \n * @for (node of nodes; track node.name) {\n *
  • \n * {{ node.name }}\n * @if (node.children) {\n *
      \n * \n * \n * \n *
    \n * }\n *
  • \n * }\n *
    \n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Tree](guide/aria/tree)\n */", + "implements": [], "source": { "filePath": "/src/aria/tree/tree.ts", - "startLine": 407, - "endLine": 451 + "startLine": 64, + "endLine": 221 } } ], @@ -617,14 +617,6 @@ "ElementRef", "@angular/core" ], - [ - "afterRenderEffect", - "@angular/core" - ], - [ - "booleanAttribute", - "@angular/core" - ], [ "computed", "@angular/core" @@ -637,18 +629,10 @@ "input", "@angular/core" ], - [ - "model", - "@angular/core" - ], [ "signal", "@angular/core" ], - [ - "Signal", - "@angular/core" - ], [ "OnInit", "@angular/core" @@ -658,43 +642,39 @@ "@angular/core" ], [ - "untracked", + "afterRenderEffect", "@angular/core" ], [ - "afterNextRender", + "booleanAttribute", "@angular/core" ], [ - "_IdGenerator", - "@angular/cdk/a11y" - ], - [ - "Directionality", - "@angular/cdk/bidi" + "model", + "@angular/core" ], [ - "ComboboxTreePattern", - "@angular/aria/private" + "Signal", + "@angular/core" ], [ - "TreeItemPattern", - "@angular/aria/private" + "afterNextRender", + "@angular/core" ], [ - "TreePattern", - "@angular/aria/private" + "_IdGenerator", + "@angular/cdk/a11y" ], [ - "DeferredContent", - "@angular/aria/private" + "untracked", + "@angular/core" ], [ - "DeferredContentAware", - "@angular/aria/private" + "Directionality", + "@angular/cdk/bidi" ], [ - "Tree", + "TreeItemGroup", "@angular/aria/tree" ], [ @@ -702,159 +682,159 @@ "@angular/aria/tree" ], [ - "TreeItemGroup", + "Tree", "@angular/aria/tree" ], [ - "Tree", + "TreeItemGroup", "@angular/aria/tree" ], [ - "Tree.element", + "TreeItemGroup.element", "@angular/aria/tree" ], [ - "Tree.id", + "TreeItemGroup.ownedBy", "@angular/aria/tree" ], [ - "Tree.orientation", + "TreeItemGroup.ngOnInit", "@angular/aria/tree" ], [ - "Tree.multi", + "TreeItemGroup.ngOnDestroy", "@angular/aria/tree" ], [ - "Tree.disabled", + "TreeItem", "@angular/aria/tree" ], [ - "Tree.selectionMode", + "TreeItem.element", "@angular/aria/tree" ], [ - "Tree.focusMode", + "TreeItem.id", "@angular/aria/tree" ], [ - "Tree.wrap", + "TreeItem.value", "@angular/aria/tree" ], [ - "Tree.softDisabled", + "TreeItem.parent", "@angular/aria/tree" ], [ - "Tree.typeaheadDelay", + "TreeItem.disabled", "@angular/aria/tree" ], [ - "Tree.values", + "TreeItem.selectable", "@angular/aria/tree" ], [ - "Tree.textDirection", + "TreeItem.expanded", "@angular/aria/tree" ], [ - "Tree.nav", + "TreeItem.label", "@angular/aria/tree" ], [ - "Tree.currentType", + "TreeItem.searchTerm", "@angular/aria/tree" ], [ - "Tree.scrollActiveItemIntoView", + "TreeItem.tree", "@angular/aria/tree" ], [ - "TreeItem", + "TreeItem.active", "@angular/aria/tree" ], [ - "TreeItem.element", + "TreeItem.level", "@angular/aria/tree" ], [ - "TreeItem.id", + "TreeItem.selected", "@angular/aria/tree" ], [ - "TreeItem.value", + "TreeItem.visible", "@angular/aria/tree" ], [ - "TreeItem.parent", + "TreeItem.ngOnInit", "@angular/aria/tree" ], [ - "TreeItem.disabled", + "TreeItem.ngOnDestroy", "@angular/aria/tree" ], [ - "TreeItem.selectable", + "Tree", "@angular/aria/tree" ], [ - "TreeItem.expanded", + "Tree.element", "@angular/aria/tree" ], [ - "TreeItem.label", + "Tree.id", "@angular/aria/tree" ], [ - "TreeItem.searchTerm", + "Tree.orientation", "@angular/aria/tree" ], [ - "TreeItem.tree", + "Tree.multi", "@angular/aria/tree" ], [ - "TreeItem.active", + "Tree.disabled", "@angular/aria/tree" ], [ - "TreeItem.level", + "Tree.selectionMode", "@angular/aria/tree" ], [ - "TreeItem.selected", + "Tree.focusMode", "@angular/aria/tree" ], [ - "TreeItem.visible", + "Tree.wrap", "@angular/aria/tree" ], [ - "TreeItem.ngOnInit", + "Tree.softDisabled", "@angular/aria/tree" ], [ - "TreeItem.ngOnDestroy", + "Tree.typeaheadDelay", "@angular/aria/tree" ], [ - "TreeItemGroup", + "Tree.values", "@angular/aria/tree" ], [ - "TreeItemGroup.element", + "Tree.textDirection", "@angular/aria/tree" ], [ - "TreeItemGroup.ownedBy", + "Tree.nav", "@angular/aria/tree" ], [ - "TreeItemGroup.ngOnInit", + "Tree.currentType", "@angular/aria/tree" ], [ - "TreeItemGroup.ngOnDestroy", + "Tree.scrollActiveItemIntoView", "@angular/aria/tree" ] ] diff --git a/adev/src/content/best-practices/a11y.md b/adev/src/content/best-practices/a11y.md index 9fb674e44a79..14e751f56025 100644 --- a/adev/src/content/best-practices/a11y.md +++ b/adev/src/content/best-practices/a11y.md @@ -10,10 +10,8 @@ This page discusses best practices for designing Angular applications that work ## Accessibility attributes - - Building accessible web experiences often involves setting [Accessible Rich Internet Applications \(ARIA\) attributes](https://web.dev/learn/accessibility/aria-html/) to provide semantic meaning where it might otherwise be missing. -Use attribute binding template syntax to control the values of accessibility-related attributes. +Use [attribute binding](guide/templates/binding#binding-dynamic-properties-and-attributes) template syntax to control the values of accessibility-related attributes. ### ARIA attributes and properties diff --git a/adev/src/content/best-practices/error-handling.md b/adev/src/content/best-practices/error-handling.md index c4cd17a7f6bb..d9cb07d367c9 100644 --- a/adev/src/content/best-practices/error-handling.md +++ b/adev/src/content/best-practices/error-handling.md @@ -37,10 +37,9 @@ export class GlobalErrorHandler implements ErrorHandler { description: `Screen: ${url} | ${errorMessage}`, }); - console.error(GlobalErrorHandler.name, { error }); + console.error(GlobalErrorHandler.name, {error}); } } - ``` ### `TestBed` rethrows errors by default diff --git a/adev/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md b/adev/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md index c78d6c28efab..67a98b65e0ea 100644 --- a/adev/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md +++ b/adev/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md @@ -34,9 +34,9 @@ Angular profiling works exclusively in development mode. Here is an example of how you can enable the integration in the application bootstrap to capture all possible events: ```ts -import { enableProfiling } from '@angular/core'; -import { bootstrapApplication } from '@angular/platform-browser'; -import { MyApp } from './my-app'; +import {enableProfiling} from '@angular/core'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {MyApp} from './my-app'; // Turn on profiling *before* bootstrapping your application // in order to capture all of the code run on start-up. diff --git a/adev/src/content/best-practices/runtime-performance/skipping-subtrees.md b/adev/src/content/best-practices/runtime-performance/skipping-subtrees.md index 39313fb006d3..02304d483b8b 100644 --- a/adev/src/content/best-practices/runtime-performance/skipping-subtrees.md +++ b/adev/src/content/best-practices/runtime-performance/skipping-subtrees.md @@ -16,7 +16,8 @@ OnPush change detection instructs Angular to run change detection for a componen You can set the change detection strategy of a component to `OnPush` in the `@Component` decorator: ```ts -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; + @Component({ changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/adev/src/content/best-practices/runtime-performance/zone-pollution.md b/adev/src/content/best-practices/runtime-performance/zone-pollution.md index 37cdf7367239..38dfda071a7a 100644 --- a/adev/src/content/best-practices/runtime-performance/zone-pollution.md +++ b/adev/src/content/best-practices/runtime-performance/zone-pollution.md @@ -21,38 +21,38 @@ In the image above, there is a series of change detection calls triggered by eve In such cases, you can instruct Angular to avoid calling change detection for tasks scheduled by a given piece of code using [NgZone](/api/core/NgZone). - -import { Component, NgZone, OnInit } from '@angular/core'; +```ts {header:"Run outside of the Zone" , linenums} +import { Component, NgZone, OnInit, inject } from '@angular/core'; @Component(...) class AppComponent implements OnInit { -private ngZone = inject(NgZone); + private ngZone = inject(NgZone); -ngOnInit() { -this.ngZone.runOutsideAngular(() => setInterval(pollForUpdates), 500); + ngOnInit() { + this.ngZone.runOutsideAngular(() => setInterval(pollForUpdates, 500)); + } } -} - +``` The preceding snippet instructs Angular to call `setInterval` outside the Angular Zone and skip running change detection after `pollForUpdates` runs. Third-party libraries commonly trigger unnecessary change detection cycles when their APIs are invoked within the Angular zone. This phenomenon particularly affects libraries that set up event listeners or initiate other tasks (such as timers, XHR requests, etc.). Avoid these extra cycles by calling library APIs outside the Angular zone: - -import { Component, NgZone, OnInit } from '@angular/core'; +```ts {header:"Move the plot initialization outside of the Zone" , linenums} +import { Component, NgZone, OnInit, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { -private ngZone = inject(NgZone); + private ngZone = inject(NgZone); -ngOnInit() { -this.ngZone.runOutsideAngular(() => { -Plotly.newPlot('chart', data); -}); -} + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + Plotly.newPlot('chart', data); + }); + } } - +``` Running `Plotly.newPlot('chart', data);` within `runOutsideAngular` instructs the framework that it shouldn’t run change detection after the execution of tasks scheduled by the initialization logic. @@ -60,24 +60,24 @@ For example, if `Plotly.newPlot('chart', data)` adds event listeners to a DOM el But sometimes, you may need to listen to events dispatched by third-party APIs. In such cases, it's important to remember that those event listeners will also execute outside of the Angular zone if the initialization logic was done there: - -import { Component, NgZone, OnInit, output } from '@angular/core'; +```ts {header:"Check whether the handler is called outside of the Zone" , linenums} +import { Component, NgZone, OnInit, output, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { -private ngZone = inject(NgZone); + private ngZone = inject(NgZone); -plotlyClick = output(); + plotlyClick = output(); -ngOnInit() { -this.ngZone.runOutsideAngular(() => { -this.createPlotly(); -}); -} + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + this.createPlotly(); + }); + } -private async createPlotly() { -const plotly = await Plotly.newPlot('chart', data); + private async createPlotly() { + const plotly = await Plotly.newPlot('chart', data); plotly.on('plotly_click', (event: Plotly.PlotMouseEvent) => { // This handler will be called outside of the Angular zone because @@ -86,40 +86,38 @@ const plotly = await Plotly.newPlot('chart', data); console.log(NgZone.isInAngularZone()); this.plotlyClick.emit(event); }); - + } } -} - +``` If you need to dispatch events to parent components and execute specific view update logic, you should consider re-entering the Angular zone to instruct the framework to run change detection or run change detection manually: - -import { Component, NgZone, OnInit, output } from '@angular/core'; +```ts {header:"Re-enter the Angular zone when dispatching event" , linenums} +import { Component, NgZone, OnInit, output, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { -private ngZone = inject(NgZone); + private ngZone = inject(NgZone); -plotlyClick = output(); + plotlyClick = output(); -ngOnInit() { -this.ngZone.runOutsideAngular(() => { -this.createPlotly(); -}); -} + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + this.createPlotly(); + }); + } -private async createPlotly() { -const plotly = await Plotly.newPlot('chart', data); + private async createPlotly() { + const plotly = await Plotly.newPlot('chart', data); plotly.on('plotly_click', (event: Plotly.PlotMouseEvent) => { this.ngZone.run(() => { this.plotlyClick.emit(event); }); }); - -} + } } - +``` The scenario of dispatching events outside of the Angular zone may also arise. It's important to remember that triggering change detection (for example, manually) may result in the creation/update of views outside of the Angular zone. diff --git a/adev/src/content/best-practices/style-guide.md b/adev/src/content/best-practices/style-guide.md index 5cffdd32a54c..6b32e7646678 100644 --- a/adev/src/content/best-practices/style-guide.md +++ b/adev/src/content/best-practices/style-guide.md @@ -110,11 +110,11 @@ When in doubt, go with the approach that leads to smaller files. ### Prefer the `inject` function over constructor parameter injection -Prefer using the `inject` function over injecting constructor parameters. The `inject` function works the same way as constructor parameter injection, but offers several style advantages: +Prefer using the [`inject`](/api/core/inject) function over injecting constructor parameters. The [`inject`](/api/core/inject) function works the same way as constructor parameter injection, but offers several style advantages: -- `inject` is generally more readable, especially when a class injects many dependencies. +- [`inject`](/api/core/inject) is generally more readable, especially when a class injects many dependencies. - It's more syntactically straightforward to add comments to injected dependencies -- `inject` offers better type inference. +- [`inject`](/api/core/inject) offers better type inference. - When targeting ES2022+ with [`useDefineForClassFields`](https://www.typescriptlang.org/tsconfig/#useDefineForClassFields), you can avoid separating field declaration and initialization when fields read on injected dependencies. [You can refactor existing code to `inject` with an automatic tool](reference/migrations/inject-function). @@ -196,7 +196,9 @@ properties initialized by `input`, `model`, `output`, and queries. The readonly ensures that the value set by Angular is not overwritten. ```ts -@Component({/* ... */}) +@Component({ + /* ... */ +}) export class UserProfile { readonly userId = input(); readonly userSaved = output(); @@ -208,7 +210,9 @@ For components and directives that use the decorator-based `@Input`, `@Output`, advice applies to output properties and queries, but not input properties. ```ts -@Component({/* ... */}) +@Component({ + /* ... */ +}) export class UserProfile { @Output() readonly userSaved = new EventEmitter(); @ViewChildren(PaymentMethod) readonly paymentMethods?: QueryList; @@ -219,18 +223,21 @@ export class UserProfile { Prefer `class` and `style` bindings over using the [`NgClass`](/api/common/NgClass) and [`NgStyle`](/api/common/NgStyle) directives. -```html - +```html {prefer}
    -
    - -
    -
    - +
    + +
    +
    +
    +
    +
    +``` - +```html {avoid}
    -
    +
    +
    ``` Both `class` and `style` bindings use a more straightforward syntax that aligns closely with @@ -246,11 +253,11 @@ For more details, refer to the [bindings guide](/guide/templates/binding#css-cla Prefer naming event handlers for the action they perform rather than for the triggering event: -```html - +```html {prefer} +``` - +```html {avoid} ``` @@ -268,8 +275,9 @@ single well-named handler. In these cases, it's fine to fall back to a name like then delegate to more specific behaviors based on the event details: ```ts - -@Component({/* ... */}) +@Component({ + /* ... */ +}) class RichText { handleKeydown(event: KeyboardEvent) { if (event.ctrlKey) { @@ -278,7 +286,7 @@ class RichText { } else if (event.key === 'I') { this.activateItalic(); } -// ... + // ... } } } @@ -291,14 +299,14 @@ well-named methods to contain that logic and then _call those methods_ in your l Lifecycle hook names describe _when_ they run, meaning that the code inside doesn't have a meaningful name that describes what the code inside is doing. -```typescript -// PREFER +```ts {prefer} ngOnInit() { this.startLogging(); this.runBackgroundTask(); } +``` -// AVOID +```ts {avoid} ngOnInit() { this.logger.setMode('info'); this.logger.monitorErrors(); @@ -314,10 +322,13 @@ your class, import and `implement` these interfaces to ensure that the methods a ```ts import {Component, OnInit} from '@angular/core'; -@Component({/* ... */}) +@Component({ + /* ... */ +}) export class UserProfile implements OnInit { - // The `OnInit` interface ensures this method is named correctly. - ngOnInit() { /* ... */ } + ngOnInit() { + /* ... */ + } } ``` diff --git a/adev/src/content/cdk/_build-info.json b/adev/src/content/cdk/_build-info.json index 8648b5dae4a1..d01e29ef1cc1 100644 --- a/adev/src/content/cdk/_build-info.json +++ b/adev/src/content/cdk/_build-info.json @@ -1,4 +1,4 @@ { "branchName": "refs/heads/main", - "sha": "7bdd46f59d03fc158d341cdd677fdb05157989d9" + "sha": "079d772c49bd8170ee4bf67bfd4bd9705503ad0e" } \ No newline at end of file diff --git a/adev/src/content/cdk/cdk_testing.json b/adev/src/content/cdk/cdk_testing.json index b5e65cadec74..a8c5cde39792 100755 --- a/adev/src/content/cdk/cdk_testing.json +++ b/adev/src/content/cdk/cdk_testing.json @@ -6808,7 +6808,7 @@ "source": { "filePath": "src/cdk/testing/component-harness.ts", "startLine": 603, - "endLine": 731 + "endLine": 726 } } ], diff --git a/adev/src/content/cdk/cdk_testing_testbed.json b/adev/src/content/cdk/cdk_testing_testbed.json index 0995bf2ee9f9..96e544225bd7 100755 --- a/adev/src/content/cdk/cdk_testing_testbed.json +++ b/adev/src/content/cdk/cdk_testing_testbed.json @@ -118,12 +118,12 @@ { "name": "waitForTasksOutsideAngular", "entryType": "function", - "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.", + "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.\n\nThis only works when Zone.js is present _and_ patches are applied to the test framework\nby `zone.js/testing` (Jasmine and Jest only) or another script.", "generics": [], "isNewType": false, "jsdocTags": [], "params": [], - "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n */", + "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n *\n * This only works when Zone.js is present _and_ patches are applied to the test framework\n * by `zone.js/testing` (Jasmine and Jest only) or another script.\n */", "returnType": "Promise" } ], @@ -133,15 +133,15 @@ "returnType": "Promise", "generics": [], "name": "waitForTasksOutsideAngular", - "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.", + "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.\n\nThis only works when Zone.js is present _and_ patches are applied to the test framework\nby `zone.js/testing` (Jasmine and Jest only) or another script.", "entryType": "function", "jsdocTags": [], - "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n */" + "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n *\n * This only works when Zone.js is present _and_ patches are applied to the test framework\n * by `zone.js/testing` (Jasmine and Jest only) or another script.\n */" }, "entryType": "function", - "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.", + "description": "Waits for all scheduled or running async tasks to complete. This allows harness\nauthors to wait for async tasks outside of the Angular zone.\n\nThis only works when Zone.js is present _and_ patches are applied to the test framework\nby `zone.js/testing` (Jasmine and Jest only) or another script.", "jsdocTags": [], - "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n */", + "rawComment": "/**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n *\n * This only works when Zone.js is present _and_ patches are applied to the test framework\n * by `zone.js/testing` (Jasmine and Jest only) or another script.\n */", "memberType": "method", "memberTags": [] }, @@ -613,7 +613,7 @@ "source": { "filePath": "/src/cdk/testing/testbed/testbed-harness-environment.ts", "startLine": 89, - "endLine": 222 + "endLine": 225 } }, { diff --git a/adev/src/content/cli/BUILD.bazel b/adev/src/content/cli/BUILD.bazel index b899cc30b5ad..759ae34f8a04 100644 --- a/adev/src/content/cli/BUILD.bazel +++ b/adev/src/content/cli/BUILD.bazel @@ -1,14 +1,23 @@ +load("//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html.bzl", "render_api_to_html") + package(default_visibility = ["//visibility:public"]) +exports_files(["_build-info.json"]) + filegroup( name = "cli", srcs = glob( - ["**"], + ["*.json"], exclude = [ - "BUILD.bazel", + # Exclude _build-info.json as it is not a help entry. + "_build-info.json", ], - ) + [ - "//adev/src/content/cli/help", - "//adev/src/content/cli/help:_build-info.json", + ), +) + +render_api_to_html( + name = "cli_docs", + srcs = [ + ":cli", ], ) diff --git a/adev/src/content/cli/_build-info.json b/adev/src/content/cli/_build-info.json new file mode 100644 index 000000000000..6c1fa2ed6f15 --- /dev/null +++ b/adev/src/content/cli/_build-info.json @@ -0,0 +1,4 @@ +{ + "branchName": "refs/heads/main", + "sha": "1814da6d24173573f12224b345c81bbb80b7a7b7" +} \ No newline at end of file diff --git a/adev/src/content/cli/help/add.json b/adev/src/content/cli/add.json similarity index 100% rename from adev/src/content/cli/help/add.json rename to adev/src/content/cli/add.json diff --git a/adev/src/content/cli/help/analytics.json b/adev/src/content/cli/analytics.json similarity index 100% rename from adev/src/content/cli/help/analytics.json rename to adev/src/content/cli/analytics.json diff --git a/adev/src/content/cli/help/build.json b/adev/src/content/cli/build.json similarity index 100% rename from adev/src/content/cli/help/build.json rename to adev/src/content/cli/build.json diff --git a/adev/src/content/cli/help/cache.json b/adev/src/content/cli/cache.json similarity index 100% rename from adev/src/content/cli/help/cache.json rename to adev/src/content/cli/cache.json diff --git a/adev/src/content/cli/help/completion.json b/adev/src/content/cli/completion.json similarity index 100% rename from adev/src/content/cli/help/completion.json rename to adev/src/content/cli/completion.json diff --git a/adev/src/content/cli/help/config.json b/adev/src/content/cli/config.json similarity index 100% rename from adev/src/content/cli/help/config.json rename to adev/src/content/cli/config.json diff --git a/adev/src/content/cli/help/deploy.json b/adev/src/content/cli/deploy.json similarity index 100% rename from adev/src/content/cli/help/deploy.json rename to adev/src/content/cli/deploy.json diff --git a/adev/src/content/cli/help/e2e.json b/adev/src/content/cli/e2e.json similarity index 100% rename from adev/src/content/cli/help/e2e.json rename to adev/src/content/cli/e2e.json diff --git a/adev/src/content/cli/help/extract-i18n.json b/adev/src/content/cli/extract-i18n.json similarity index 100% rename from adev/src/content/cli/help/extract-i18n.json rename to adev/src/content/cli/extract-i18n.json diff --git a/adev/src/content/cli/help/generate.json b/adev/src/content/cli/generate.json similarity index 100% rename from adev/src/content/cli/help/generate.json rename to adev/src/content/cli/generate.json diff --git a/adev/src/content/cli/help/BUILD.bazel b/adev/src/content/cli/help/BUILD.bazel deleted file mode 100644 index f7a6207855f0..000000000000 --- a/adev/src/content/cli/help/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html.bzl", "render_api_to_html") - -package(default_visibility = ["//visibility:public"]) - -exports_files(["_build-info.json"]) - -filegroup( - name = "help", - srcs = glob( - ["*.json"], - exclude = [ - # Exlucde _build-info.json as it is not a help entry. - "_*.json", - ], - ), -) - -render_api_to_html( - name = "cli_docs", - srcs = [ - ":help", - ], -) diff --git a/adev/src/content/cli/help/_build-info.json b/adev/src/content/cli/help/_build-info.json deleted file mode 100644 index 0de919ce624d..000000000000 --- a/adev/src/content/cli/help/_build-info.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "branchName": "refs/heads/main", - "sha": "bc00b95ab3e2db817b1fb7ef0c491c8fc3c17523" -} \ No newline at end of file diff --git a/adev/src/content/cli/index.md b/adev/src/content/cli/index.md deleted file mode 100644 index d6c5d06146e3..000000000000 --- a/adev/src/content/cli/index.md +++ /dev/null @@ -1,136 +0,0 @@ -# CLI Overview and Command Reference - -The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell. - -## Installing Angular CLI - -Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately. - -Install the CLI using the `npm` package manager: - - - -npm install -g @angular/cli - - - -For details about changes between versions, and information about updating from previous releases, see the Releases tab on GitHub: https://github.com/angular/angular-cli/releases - -## Basic workflow - -Invoke the tool on the command line through the `ng` executable. -Online help is available on the command line. -Enter the following to list commands or options for a given command \(such as [new](cli/new)\) with a short description. - - - -ng --help -ng new --help - - - -To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands: - - - -ng new my-first-project -cd my-first-project -ng serve - - - -In your browser, open http://localhost:4200/ to see the new application run. -When you use the [ng serve](cli/serve) command to build an application and serve it locally, the server automatically rebuilds the application and reloads the page when you change any of the source files. - -
    - -When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory. -Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command. - -If the current working directory is not the right place for your project, you can change to a more appropriate directory by running `cd `. - -
    - -## Workspaces and project files - -The [ng new](cli/new) command creates an _Angular workspace_ folder and generates a new application skeleton. -A workspace can contain multiple applications and libraries. -The initial application created by the [ng new](cli/new) command is at the top level of the workspace. -When you generate an additional application or library in a workspace, it goes into a `projects/` subfolder. - -A newly generated application contains the source files for a root module, with a root component and template. -Each application has a `src` folder that contains the logic, data, and assets. - -You can edit the generated files directly, or add to and modify them using CLI commands. -Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on. -Commands such as [add](cli/add) and [generate](cli/generate), which create or operate on applications and libraries, must be executed from within a workspace or project folder. - -- See more about the [Workspace file structure](guide/file-structure). - -### Workspace and project configuration - -A single workspace configuration file, `angular.json`, is created at the top level of the workspace. -This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets. - -The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly. - -
    - -**NOTE**:
    -Option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands must be dash-case. - -
    - -- See more about [Workspace Configuration](guide/workspace-config). - -## CLI command-language syntax - -Command syntax is shown as follows: - -`ng` __ __ [*optional-arg*] `[options]` - -- Most commands, and some options, have aliases. - Aliases are shown in the syntax statement for each command. - -- Option names are prefixed with a double dash \(`--`\) characters. - Option aliases are prefixed with a single dash \(`-`\) character. - Arguments are not prefixed. - For example: - - - - ng build my-app -c production - - - -- Typically, the name of a generated artifact can be given as an argument to the command or specified with the `--name` option. - -- Arguments and option names must be given in [dash-case](guide/glossary#case-types). - For example: `--my-option-name` - -### Boolean options - -Boolean options have two forms: `--this-option` sets the flag to `true`, `--no-this-option` sets it to `false`. -If neither option is supplied, the flag remains in its default state, as listed in the reference documentation. - -### Array options - -Array options can be provided in two forms: `--option value1 value2` or `--option value1 --option value2`. - -### Relative paths - -Options that specify files can be given as absolute paths, or as paths relative to the current working directory, which is generally either the workspace or project root. - -### Schematics - -The [ng generate](cli/generate) and [ng add](cli/add) commands take, as an argument, the artifact or library to be generated or added to the current project. -In addition to any general options, each artifact or library defines its own options in a _schematic_. -Schematic options are supplied to the command in the same format as immediate command options. - - - - - - - -@reviewed 2022-02-28 diff --git a/adev/src/content/cli/help/lint.json b/adev/src/content/cli/lint.json similarity index 100% rename from adev/src/content/cli/help/lint.json rename to adev/src/content/cli/lint.json diff --git a/adev/src/content/cli/help/new.json b/adev/src/content/cli/new.json similarity index 100% rename from adev/src/content/cli/help/new.json rename to adev/src/content/cli/new.json diff --git a/adev/src/content/cli/help/run.json b/adev/src/content/cli/run.json similarity index 100% rename from adev/src/content/cli/help/run.json rename to adev/src/content/cli/run.json diff --git a/adev/src/content/cli/help/serve.json b/adev/src/content/cli/serve.json similarity index 100% rename from adev/src/content/cli/help/serve.json rename to adev/src/content/cli/serve.json diff --git a/adev/src/content/cli/help/test.json b/adev/src/content/cli/test.json similarity index 99% rename from adev/src/content/cli/help/test.json rename to adev/src/content/cli/test.json index 7b9af75fcd2d..6397e7aa095d 100644 --- a/adev/src/content/cli/help/test.json +++ b/adev/src/content/cli/test.json @@ -153,7 +153,6 @@ { "name": "ui", "type": "boolean", - "default": false, "description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner." }, { diff --git a/adev/src/content/cli/help/update.json b/adev/src/content/cli/update.json similarity index 100% rename from adev/src/content/cli/help/update.json rename to adev/src/content/cli/update.json diff --git a/adev/src/content/cli/help/version.json b/adev/src/content/cli/version.json similarity index 100% rename from adev/src/content/cli/help/version.json rename to adev/src/content/cli/version.json diff --git a/adev/src/content/ecosystem/rxjs-interop/output-interop.md b/adev/src/content/ecosystem/rxjs-interop/output-interop.md index bd9faff699cf..7df65c45e782 100644 --- a/adev/src/content/ecosystem/rxjs-interop/output-interop.md +++ b/adev/src/content/ecosystem/rxjs-interop/output-interop.md @@ -12,12 +12,14 @@ The `outputFromObservable` lets you create a component or directive output that import {Directive} from '@angular/core'; import {outputFromObservable} from '@angular/core/rxjs-interop'; -@Directive({/*...*/}) +@Directive({ + /*...*/ +}) class Draggable { - pointerMoves$: Observable = listenToPointerMoves(); + pointerMoves$: Observable = listenToPointerMoves(); - // Whenever `pointerMoves$` emits, the `pointerMove` event fires. - pointerMove = outputFromObservable(this.pointerMoves$); + // Whenever `pointerMoves$` emits, the `pointerMove` event fires. + pointerMove = outputFromObservable(this.pointerMoves$); } ``` diff --git a/adev/src/content/ecosystem/rxjs-interop/signals-interop.md b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md index 2ab060e0cdfd..ee763e37afc5 100644 --- a/adev/src/content/ecosystem/rxjs-interop/signals-interop.md +++ b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -62,20 +62,20 @@ Some observables may emit values that are **equals** even though they differ by When two emitted values are considered equal, the resulting signal **does not update**. This prevents redundant computations, DOM updates, or effects from re-running unnecessarily. ```ts -import { Component } from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; -import { interval, map } from 'rxjs'; +import {Component} from '@angular/core'; +import {toSignal} from '@angular/core/rxjs-interop'; +import {interval, map} from 'rxjs'; @Component(/* ... */) export class EqualExample { temperature$ = interval(1000).pipe( - map(() => ({ temperature: Math.floor(Math.random() * 3) + 20 }) ) // 20, 21, or 22 randomly + map(() => ({temperature: Math.floor(Math.random() * 3) + 20})), // 20, 21, or 22 randomly ); // Only update if the temperature changes temperature = toSignal(this.temperature$, { - initialValue: { temperature : 20 }, - equal: (prev, curr) => prev.temperature === curr.temperature + initialValue: {temperature: 20}, + equal: (prev, curr) => prev.temperature === curr.temperature, }); } ``` @@ -91,17 +91,15 @@ If an Observable used in `toSignal` completes, the signal continues to return th Use the `toObservable` utility to create an `Observable` which tracks the value of a signal. The signal's value is monitored with an `effect` which emits the value to the Observable when it changes. ```ts -import { Component, signal } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; +import {Component, signal} from '@angular/core'; +import {toObservable} from '@angular/core/rxjs-interop'; @Component(/* ... */) export class SearchResults { query: Signal = inject(QueryService).query; query$ = toObservable(this.query); - results$ = this.query$.pipe( - switchMap(query => this.http.get('/search?q=' + query )) - ); + results$ = this.query$.pipe(switchMap((query) => this.http.get('/search?q=' + query))); } ``` @@ -119,7 +117,7 @@ Unlike Observables, signals never provide a synchronous notification of changes. ```ts const obs$ = toObservable(mySignal); -obs$.subscribe(value => console.log(value)); +obs$.subscribe((value) => console.log(value)); mySignal.set(1); mySignal.set(2); @@ -146,7 +144,7 @@ export class UserProfile { protected userId = input(); private userResource = rxResource({ - params: () => ({ userId: this.userId() }), + params: () => ({userId: this.userId()}), // The `stream` property expects a factory function that returns // a data stream as an RxJS Observable. diff --git a/adev/src/content/ecosystem/rxjs-interop/take-until-destroyed.md b/adev/src/content/ecosystem/rxjs-interop/take-until-destroyed.md index 19e8a4294b5f..fc94b13d506e 100644 --- a/adev/src/content/ecosystem/rxjs-interop/take-until-destroyed.md +++ b/adev/src/content/ecosystem/rxjs-interop/take-until-destroyed.md @@ -18,7 +18,7 @@ export class UserProfile { // This subscription the 'notifications' Observable is automatically // unsubscribed when the 'UserProfile' component is destroyed. const messages: Observable = this.dispatcher.notifications; - messages.pipe(takeUntilDestroyed()).subscribe(message => { + messages.pipe(takeUntilDestroyed()).subscribe((message) => { this.popup.show(message); }); } @@ -38,7 +38,7 @@ export class UserProfile { // Always pass a `DestroyRef` if you call `takeUntilDestroyed` outside // of an injection context. const messages: Observable = this.dispatcher.notifications; - messages.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(message => { + messages.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((message) => { this.popup.show(message); }); } diff --git a/adev/src/content/ecosystem/service-workers/app-shell.md b/adev/src/content/ecosystem/service-workers/app-shell.md index 31e86a0a83d4..7a7d9afc4cad 100644 --- a/adev/src/content/ecosystem/service-workers/app-shell.md +++ b/adev/src/content/ecosystem/service-workers/app-shell.md @@ -26,7 +26,7 @@ For more information about this command, see [App shell command](cli/generate/ap The command updates the application code and adds extra files to the project structure. - +```text src ├── app │ ├── app.config.server.ts # server application configuration @@ -36,7 +36,7 @@ src │ ├── app-shell.component.spec.ts │ └── app-shell.component.ts └── main.server.ts # main server application bootstrapping - +``` diff --git a/adev/src/content/ecosystem/service-workers/communications.md b/adev/src/content/ecosystem/service-workers/communications.md index 71e9ffe51b51..22822c68f262 100644 --- a/adev/src/content/ecosystem/service-workers/communications.md +++ b/adev/src/content/ecosystem/service-workers/communications.md @@ -24,7 +24,7 @@ The `versionUpdates` is an `Observable` property of `SwUpdate` and emits five ev | `VersionInstallationFailedEvent` | Emitted when the installation of a new version failed. It may be used for logging/monitoring purposes. | | `VersionFailedEvent` | Emitted when a version encounters a critical failure (such as broken hash errors) that affects all clients using that version. Provides error details for debugging and transparency. | - + ### Checking for updates @@ -56,7 +56,7 @@ Alternatively, you might want to define a different [registration strategy](api/ You can update an existing tab to the latest version by reloading the page as soon as a new version is ready. To avoid disrupting the user's progress, it is generally a good idea to prompt the user and let them confirm that it is OK to reload the page and update to the latest version: - + Calling `activateUpdate()` updates a tab to the latest version without reloading the page, but this could break the application. @@ -96,7 +96,7 @@ That particular application version is broken and there is no way to fix the sta In such cases, the service worker notifies the client by sending an `UnrecoverableStateEvent` event. Subscribe to `SwUpdate#unrecoverable` to be notified and handle these errors. - + ## More on Angular service workers diff --git a/adev/src/content/ecosystem/service-workers/config.md b/adev/src/content/ecosystem/service-workers/config.md index 8f1333947f55..9a6500f88754 100644 --- a/adev/src/content/ecosystem/service-workers/config.md +++ b/adev/src/content/ecosystem/service-workers/config.md @@ -354,14 +354,12 @@ The URL query is ignored when matching. If the field is omitted, it defaults to: ```ts - [ -'/**', // Include all URLs. -'!/**/*.*', // Exclude URLs to files (containing a file extension in the last segment). -'!/**/*__*', // Exclude URLs containing `__` in the last segment. -'!/**/*__*/**', // Exclude URLs containing `__` in any other segment. -] - + '/**', // Include all URLs. + '!/**/*.*', // Exclude URLs to files (containing a file extension in the last segment). + '!/**/*__*', // Exclude URLs containing `__` in the last segment. + '!/**/*__*/**', // Exclude URLs containing `__` in any other segment. +]; ``` ### `navigationRequestStrategy` @@ -369,7 +367,6 @@ If the field is omitted, it defaults to: This optional property enables you to configure how the service worker handles navigation requests: ```json - { "navigationRequestStrategy": "freshness" } diff --git a/adev/src/content/ecosystem/service-workers/custom-service-worker-scripts.md b/adev/src/content/ecosystem/service-workers/custom-service-worker-scripts.md index d5fdcaa6ddbc..2a5dbaeac9f6 100644 --- a/adev/src/content/ecosystem/service-workers/custom-service-worker-scripts.md +++ b/adev/src/content/ecosystem/service-workers/custom-service-worker-scripts.md @@ -73,8 +73,8 @@ importScripts('./ngsw-worker.js'); 3. Configure the service worker registration to use your custom script: ```ts -import { ApplicationConfig, isDevMode } from '@angular/core'; -import { provideServiceWorker } from '@angular/service-worker'; +import {ApplicationConfig, isDevMode} from '@angular/core'; +import {provideServiceWorker} from '@angular/service-worker'; export const appConfig: ApplicationConfig = { providers: [ diff --git a/adev/src/content/ecosystem/service-workers/devops.md b/adev/src/content/ecosystem/service-workers/devops.md index fb3b0ea92104..a2fc1876eac2 100644 --- a/adev/src/content/ecosystem/service-workers/devops.md +++ b/adev/src/content/ecosystem/service-workers/devops.md @@ -138,7 +138,7 @@ The Angular service worker exposes debugging information under the `ngsw/` virtu Currently, the single exposed URL is `ngsw/state`. Here is an example of this debug page's contents: - +```shell {hideCopy} NGSW Debug Info: @@ -160,17 +160,17 @@ Task queue: Debug log: - +``` #### Driver state The first line indicates the driver state: - +```shell {hideCopy} Driver state: NORMAL ((nominal)) - +``` `NORMAL` indicates that the service worker is operating normally and is not in a degraded state. @@ -190,21 +190,21 @@ The new instance starts in the `NORMAL` mode, regardless of the state of the pre #### Latest manifest hash - +```shell {hideCopy} Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c - +``` This is the SHA1 hash of the most up-to-date version of the application that the service worker knows about. #### Last update check - +```shell {hideCopy} Last update check: never - +``` This indicates the last time the service worker checked for a new version, or update, of the application. `never` indicates that the service worker has never checked for an update. @@ -213,13 +213,13 @@ In this example debug file, the update check is currently scheduled, as explaine #### Version - +```shell {hideCopy} === Version eea7f5f464f90789b621170af5a569d6be077e5c === Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65 - +``` In this example, the service worker has one version of the application cached and being used to serve two different tabs. @@ -227,7 +227,7 @@ HELPFUL: This version hash is the "latest manifest hash" listed above. Both clie #### Idle task queue - +```shell {hideCopy} === Idle Task Queue === Last update tick: 1s496u @@ -236,7 +236,7 @@ Task queue: - init post-load (update, cleanup) - +``` The Idle Task Queue is the queue of all pending tasks that happen in the background in the service worker. If there are any tasks in the queue, they are listed with a description. @@ -248,11 +248,11 @@ The "Last update run" counter shows the last time idle tasks were actually execu #### Debug log - +```shell {hideCopy} Debug log: - +``` Errors that occur within the service worker are logged here. diff --git a/adev/src/content/ecosystem/service-workers/getting-started.md b/adev/src/content/ecosystem/service-workers/getting-started.md index 268a3a187042..a99bb75ca797 100644 --- a/adev/src/content/ecosystem/service-workers/getting-started.md +++ b/adev/src/content/ecosystem/service-workers/getting-started.md @@ -166,9 +166,8 @@ Angular service workers support comprehensive configuration options through the The `enabled` option controls whether the service worker will be registered and related services will attempt to communicate with it. ```ts - -import { ApplicationConfig, isDevMode } from '@angular/core'; -import { provideServiceWorker } from '@angular/service-worker'; +import {ApplicationConfig, isDevMode} from '@angular/core'; +import {provideServiceWorker} from '@angular/service-worker'; export const appConfig: ApplicationConfig = { providers: [ @@ -177,7 +176,6 @@ export const appConfig: ApplicationConfig = { }), ], }; - ``` ### Cache control with updateViaCache @@ -185,7 +183,6 @@ export const appConfig: ApplicationConfig = { The `updateViaCache` option controls how the browser consults the HTTP cache during service worker updates. This provides fine-grained control over when the browser fetches updated service worker scripts and imported modules. ```ts - export const appConfig: ApplicationConfig = { providers: [ provideServiceWorker('ngsw-worker.js', { @@ -194,7 +191,6 @@ export const appConfig: ApplicationConfig = { }), ], }; - ``` The `updateViaCache` option accepts the following values: @@ -208,7 +204,6 @@ The `updateViaCache` option accepts the following values: The `type` option enables specifying the script type when registering service workers, providing support for ES module features in your service worker scripts. ```ts - export const appConfig: ApplicationConfig = { providers: [ provideServiceWorker('ngsw-worker.js', { @@ -217,7 +212,6 @@ export const appConfig: ApplicationConfig = { }), ], }; - ``` The `type` option accepts the following values: @@ -230,7 +224,6 @@ The `type` option accepts the following values: The `scope` option defines the service worker's registration scope, determining what range of URLs it can control. ```ts - export const appConfig: ApplicationConfig = { providers: [ provideServiceWorker('ngsw-worker.js', { @@ -239,7 +232,6 @@ export const appConfig: ApplicationConfig = { }), ], }; - ``` - Controls which URLs the service worker can intercept and manage @@ -251,7 +243,6 @@ export const appConfig: ApplicationConfig = { The `registrationStrategy` option defines when the service worker will be registered with the browser, providing control over the timing of registration. ```ts - export const appConfig: ApplicationConfig = { providers: [ provideServiceWorker('ngsw-worker.js', { @@ -260,7 +251,6 @@ export const appConfig: ApplicationConfig = { }), ], }; - ``` Available registration strategies: @@ -270,7 +260,6 @@ Available registration strategies: - **`'registerWithDelay:timeout'`** - Register with a delay of the specified timeout in milliseconds ```ts - // Register immediately export const immediateConfig: ApplicationConfig = { providers: [ @@ -290,13 +279,12 @@ export const delayedConfig: ApplicationConfig = { }), ], }; - ``` You can also provide an Observable factory function for custom registration timing: ```ts -import { timer } from 'rxjs'; +import {timer} from 'rxjs'; export const customConfig: ApplicationConfig = { providers: [ @@ -306,7 +294,6 @@ export const customConfig: ApplicationConfig = { }), ], }; - ``` ## More on Angular service workers diff --git a/adev/src/content/ecosystem/web-workers.md b/adev/src/content/ecosystem/web-workers.md index 9ebd9fcdcdba..74ba7736397a 100644 --- a/adev/src/content/ecosystem/web-workers.md +++ b/adev/src/content/ecosystem/web-workers.md @@ -26,29 +26,26 @@ The command performs the following actions. 1. Adds the following scaffold code to `src/app/app.worker.ts` to receive messages. ```ts {header:"src/app/app.worker.ts"} - - addEventListener('message', ({ data }) => { - const response = `worker response to ${data}`; - postMessage(response); - }); - + addEventListener('message', ({data}) => { + const response = `worker response to ${data}`; + postMessage(response); + }); ``` 1. Adds the following scaffold code to `src/app/app.component.ts` to use the worker. ```ts {header:"src/app/app.component.ts"} - - if (typeof Worker !== 'undefined') { - // Create a new - const worker = new Worker(new URL('./app.worker', import.meta.url)); - worker.onmessage = ({ data }) => { - console.log(`page got message: ${data}`); - }; - worker.postMessage('hello'); - } else { - // Web workers are not supported in this environment. - // You should add a fallback so that your program still executes correctly. - } + if (typeof Worker !== 'undefined') { + // Create a new + const worker = new Worker(new URL('./app.worker', import.meta.url)); + worker.onmessage = ({data}) => { + console.log(`page got message: ${data}`); + }; + worker.postMessage('hello'); + } else { + // Web workers are not supported in this environment. + // You should add a fallback so that your program still executes correctly. + } ``` After you create this initial scaffold, you must refactor your code to use the web worker by sending messages to and from the worker. diff --git a/adev/src/content/examples/accessibility/src/app/app.component.html b/adev/src/content/examples/accessibility/src/app/app.component.html index 69984ba3d0bf..f19941522638 100755 --- a/adev/src/content/examples/accessibility/src/app/app.component.html +++ b/adev/src/content/examples/accessibility/src/app/app.component.html @@ -2,12 +2,17 @@

    Accessibility Example

    - diff --git a/adev/src/content/examples/accessibility/src/index.html b/adev/src/content/examples/accessibility/src/index.html index c0c05cb94bbf..d2a06bff9c4d 100644 --- a/adev/src/content/examples/accessibility/src/index.html +++ b/adev/src/content/examples/accessibility/src/index.html @@ -1,13 +1,13 @@ - - - Accessibility Example - - - - - - Loading... - + + + Accessibility Example + + + + + + Loading... + diff --git a/adev/src/content/examples/angular-compiler-options/src/index.html b/adev/src/content/examples/angular-compiler-options/src/index.html index 9a54a12cd4d7..c86cefc129a8 100644 --- a/adev/src/content/examples/angular-compiler-options/src/index.html +++ b/adev/src/content/examples/angular-compiler-options/src/index.html @@ -1,13 +1,13 @@ - + - - - Ponyracer - - - - - - - + + + Ponyracer + + + + + + + diff --git a/adev/src/content/examples/angular-linker-plugin/webpack.config.mjs b/adev/src/content/examples/angular-linker-plugin/webpack.config.mjs index 430a57d171b1..697f3ebf0141 100644 --- a/adev/src/content/examples/angular-linker-plugin/webpack.config.mjs +++ b/adev/src/content/examples/angular-linker-plugin/webpack.config.mjs @@ -15,12 +15,12 @@ export default { plugins: [linkerPlugin], compact: false, cacheDirectory: true, - } - } - } - ] - } + }, + }, + }, + ], + }, // #enddocregion webpack-config // #docregion webpack-config -} +}; // #enddocregion webpack-config diff --git a/adev/src/content/examples/animations/src/app/about.component.html b/adev/src/content/examples/animations/src/app/about.component.html index 6ef2412703a9..4eb8b6b48317 100755 --- a/adev/src/content/examples/animations/src/app/about.component.html +++ b/adev/src/content/examples/animations/src/app/about.component.html @@ -1,3 +1,4 @@

    - Angular's animations library makes it easy to define and apply animation effects such as page and list transitions. + Angular's animations library makes it easy to define and apply animation effects such as page and + list transitions.

    diff --git a/adev/src/content/examples/animations/src/app/animations-package/reorder.component.html b/adev/src/content/examples/animations/src/app/animations-package/reorder.component.html index 118ded129c0f..d6d6e0729aa6 100644 --- a/adev/src/content/examples/animations/src/app/animations-package/reorder.component.html +++ b/adev/src/content/examples/animations/src/app/animations-package/reorder.component.html @@ -3,7 +3,7 @@

    Reordering List Example

      - @for(item of items; track item) { + @for (item of items; track item) {
    • {{ item }}
    • }
    diff --git a/adev/src/content/examples/animations/src/app/animations-package/stagger.component.html b/adev/src/content/examples/animations/src/app/animations-package/stagger.component.html index 6ecd4ef19e31..7cee6b0cfed8 100644 --- a/adev/src/content/examples/animations/src/app/animations-package/stagger.component.html +++ b/adev/src/content/examples/animations/src/app/animations-package/stagger.component.html @@ -2,8 +2,7 @@

    Stagger Example

      - @for(item of items; track item) { + @for (item of items; track item) {
    • {{ item }}
    • }
    - diff --git a/adev/src/content/examples/animations/src/app/app.component.html b/adev/src/content/examples/animations/src/app/app.component.html index 59e6584e9733..bca8e7f58569 100644 --- a/adev/src/content/examples/animations/src/app/app.component.html +++ b/adev/src/content/examples/animations/src/app/app.component.html @@ -1,23 +1,55 @@

    Animations

    - + diff --git a/adev/src/content/examples/animations/src/app/hero-list-auto.component.html b/adev/src/content/examples/animations/src/app/hero-list-auto.component.html index d8d7f78e8ffb..d0b96ba8c537 100644 --- a/adev/src/content/examples/animations/src/app/hero-list-auto.component.html +++ b/adev/src/content/examples/animations/src/app/hero-list-auto.component.html @@ -1,7 +1,6 @@
      @for (hero of heroes(); track hero) { -
    • +
    • - @if (isShown()) {

      The box is inserted

      diff --git a/adev/src/content/examples/animations/src/app/native-css/reorder.component.html b/adev/src/content/examples/animations/src/app/native-css/reorder.component.html index be0b923ce5d5..9c65a23360f2 100644 --- a/adev/src/content/examples/animations/src/app/native-css/reorder.component.html +++ b/adev/src/content/examples/animations/src/app/native-css/reorder.component.html @@ -3,7 +3,7 @@

      Reordering List Example

        - @for(item of items; track item) { + @for (item of items; track item) {
      • {{ item }}
      • }
      diff --git a/adev/src/content/examples/animations/src/app/native-css/stagger.component.html b/adev/src/content/examples/animations/src/app/native-css/stagger.component.html index 049550da6ae8..66071bccbb30 100644 --- a/adev/src/content/examples/animations/src/app/native-css/stagger.component.html +++ b/adev/src/content/examples/animations/src/app/native-css/stagger.component.html @@ -3,8 +3,8 @@

      Stagger Example

      @if (show()) {
        - @for(item of items; track item) { -
      • {{item}}
      • + @for (item of items; track item) { +
      • {{ item }}
      • }
      } diff --git a/adev/src/content/examples/animations/src/app/open-close-page.component.ts b/adev/src/content/examples/animations/src/app/open-close-page.component.ts index 8dae2fb20a00..30ea224c68a0 100755 --- a/adev/src/content/examples/animations/src/app/open-close-page.component.ts +++ b/adev/src/content/examples/animations/src/app/open-close-page.component.ts @@ -6,7 +6,7 @@ import {OpenCloseComponent} from './open-close.component'; template: `

      Open Close Component

      - + diff --git a/adev/src/content/examples/animations/src/app/open-close.component.2.html b/adev/src/content/examples/animations/src/app/open-close.component.2.html index 1a8e22b3c538..dca6aba158ee 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.2.html +++ b/adev/src/content/examples/animations/src/app/open-close.component.2.html @@ -5,8 +5,8 @@
      - +

      The box is now {{ isOpen ? 'Open' : 'Closed' }}!

      - +
      diff --git a/adev/src/content/examples/animations/src/app/open-close.component.3.html b/adev/src/content/examples/animations/src/app/open-close.component.3.html index b83984577574..04dbcb898180 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.3.html +++ b/adev/src/content/examples/animations/src/app/open-close.component.3.html @@ -4,12 +4,14 @@ -
      - +
      +

      The box is now {{ isOpen ? 'Open' : 'Closed' }}!

      - +
      diff --git a/adev/src/content/examples/animations/src/app/open-close.component.4.html b/adev/src/content/examples/animations/src/app/open-close.component.4.html index a2ee2e27eeca..c92142fb778c 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.4.html +++ b/adev/src/content/examples/animations/src/app/open-close.component.4.html @@ -4,9 +4,8 @@
      -
      +

      The box is now {{ isOpen ? 'Open' : 'Closed' }}!

      - \ No newline at end of file + diff --git a/adev/src/content/examples/animations/src/app/open-close.component.html b/adev/src/content/examples/animations/src/app/open-close.component.html index 4ecd2be0b3b1..1584055c97c7 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.html +++ b/adev/src/content/examples/animations/src/app/open-close.component.html @@ -1,8 +1,10 @@ -
      + class="open-close-container" +>

      The box is now {{ isOpen ? 'Open' : 'Closed' }}!

      diff --git a/adev/src/content/examples/animations/src/app/querying.component.ts b/adev/src/content/examples/animations/src/app/querying.component.ts index cc52b01eb07e..db7ae2168213 100755 --- a/adev/src/content/examples/animations/src/app/querying.component.ts +++ b/adev/src/content/examples/animations/src/app/querying.component.ts @@ -16,7 +16,9 @@ import {HEROES} from './mock-heroes'; selector: 'app-querying', template: ` @if (show) {
      diff --git a/adev/src/content/examples/animations/src/index.html b/adev/src/content/examples/animations/src/index.html index c1078687e7af..2726ab08f827 100644 --- a/adev/src/content/examples/animations/src/index.html +++ b/adev/src/content/examples/animations/src/index.html @@ -1,14 +1,13 @@ - + - - + + Animations - - + + - diff --git a/adev/src/content/examples/aria/accordion/src/disabled-focusable/material/app/app.html b/adev/src/content/examples/aria/accordion/src/disabled-focusable/material/app/app.html index 21488cdc9abb..e433b2933853 100644 --- a/adev/src/content/examples/aria/accordion/src/disabled-focusable/material/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/disabled-focusable/material/app/app.html @@ -6,7 +6,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger1.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -27,7 +28,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger2.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -48,7 +50,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger3.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      diff --git a/adev/src/content/examples/aria/accordion/src/disabled-focusable/retro/app/app.html b/adev/src/content/examples/aria/accordion/src/disabled-focusable/retro/app/app.html index 0198adf6164d..17704c597722 100644 --- a/adev/src/content/examples/aria/accordion/src/disabled-focusable/retro/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/disabled-focusable/retro/app/app.html @@ -2,11 +2,9 @@

      Unlock Treasure Box - +

      @@ -18,11 +16,9 @@

      Unlock Treasure Box - +

      @@ -34,11 +30,9 @@

      Unlock Treasure Box - +

      diff --git a/adev/src/content/examples/aria/accordion/src/multi-expansion/material/app/app.html b/adev/src/content/examples/aria/accordion/src/multi-expansion/material/app/app.html index 321b812a6755..427516aa7d85 100644 --- a/adev/src/content/examples/aria/accordion/src/multi-expansion/material/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/multi-expansion/material/app/app.html @@ -6,7 +6,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger1.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -27,7 +28,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger2.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -48,7 +50,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger3.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      diff --git a/adev/src/content/examples/aria/accordion/src/multi-expansion/retro/app/app.html b/adev/src/content/examples/aria/accordion/src/multi-expansion/retro/app/app.html index 13b37020efd0..8bf9feec945a 100644 --- a/adev/src/content/examples/aria/accordion/src/multi-expansion/retro/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/multi-expansion/retro/app/app.html @@ -2,11 +2,9 @@

      Unlock Treasure Box - +

      @@ -18,11 +16,9 @@

      Unlock Treasure Box - +

      @@ -34,11 +30,9 @@

      Unlock Treasure Box - +

      diff --git a/adev/src/content/examples/aria/accordion/src/single-expansion/material/app/app.html b/adev/src/content/examples/aria/accordion/src/single-expansion/material/app/app.html index 39b90a7c3364..acc3c6f34c9f 100644 --- a/adev/src/content/examples/aria/accordion/src/single-expansion/material/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/single-expansion/material/app/app.html @@ -6,7 +6,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger1.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -27,7 +28,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger2.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      @@ -48,7 +50,8 @@

      aria-hidden="true" class="material-symbols-outlined expand-icon" [class.expand-icon__expanded]="trigger3.expanded()" - translate="no">keyboard_arrow_upkeyboard_arrow_up

      diff --git a/adev/src/content/examples/aria/accordion/src/single-expansion/retro/app/app.html b/adev/src/content/examples/aria/accordion/src/single-expansion/retro/app/app.html index 4372cfe9ac18..d1eb1b91b0d8 100644 --- a/adev/src/content/examples/aria/accordion/src/single-expansion/retro/app/app.html +++ b/adev/src/content/examples/aria/accordion/src/single-expansion/retro/app/app.html @@ -2,11 +2,9 @@

      Unlock Treasure Box - +

      @@ -18,11 +16,9 @@

      Unlock Treasure Box - +

      @@ -34,11 +30,9 @@

      Unlock Treasure Box - +

      diff --git a/adev/src/content/examples/aria/autocomplete/src/basic/app/app.html b/adev/src/content/examples/aria/autocomplete/src/basic/app/app.html index ebb07bb9e53d..583ced285ed4 100644 --- a/adev/src/content/examples/aria/autocomplete/src/basic/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/basic/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/basic/material/app/app.html b/adev/src/content/examples/aria/autocomplete/src/basic/material/app/app.html index 5189e993edb9..0f2b696936a7 100644 --- a/adev/src/content/examples/aria/autocomplete/src/basic/material/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/basic/material/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/basic/retro/app/app.html b/adev/src/content/examples/aria/autocomplete/src/basic/retro/app/app.html index 006743cc653d..f3dac2851118 100644 --- a/adev/src/content/examples/aria/autocomplete/src/basic/retro/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/basic/retro/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/highlight/app/app.html b/adev/src/content/examples/aria/autocomplete/src/highlight/app/app.html index f58da3466df4..814e3ece9f4f 100644 --- a/adev/src/content/examples/aria/autocomplete/src/highlight/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/highlight/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/highlight/material/app/app.html b/adev/src/content/examples/aria/autocomplete/src/highlight/material/app/app.html index 89efc14b2bd4..776d3676d95c 100644 --- a/adev/src/content/examples/aria/autocomplete/src/highlight/material/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/highlight/material/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/highlight/retro/app/app.html b/adev/src/content/examples/aria/autocomplete/src/highlight/retro/app/app.html index 52eab98ccf66..c3ad881c85bb 100644 --- a/adev/src/content/examples/aria/autocomplete/src/highlight/retro/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/highlight/retro/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/manual/app/app.html b/adev/src/content/examples/aria/autocomplete/src/manual/app/app.html index 727c3204219a..dd94d75f34cf 100644 --- a/adev/src/content/examples/aria/autocomplete/src/manual/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/manual/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/manual/material/app/app.html b/adev/src/content/examples/aria/autocomplete/src/manual/material/app/app.html index 206f9c8c1ae5..c0b9048f5e1a 100644 --- a/adev/src/content/examples/aria/autocomplete/src/manual/material/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/manual/material/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/autocomplete/src/manual/retro/app/app.html b/adev/src/content/examples/aria/autocomplete/src/manual/retro/app/app.html index c341c2d43a3a..3b387d4c955a 100644 --- a/adev/src/content/examples/aria/autocomplete/src/manual/retro/app/app.html +++ b/adev/src/content/examples/aria/autocomplete/src/manual/retro/app/app.html @@ -1,6 +1,8 @@
      - search + @for (country of countries(); track country) {
      - {{country}} - check + {{ country }} +
      }
      diff --git a/adev/src/content/examples/aria/combobox/src/dialog/app/app.html b/adev/src/content/examples/aria/combobox/src/dialog/app/app.html index a560131069a6..c1382a7dbaef 100644 --- a/adev/src/content/examples/aria/combobox/src/dialog/app/app.html +++ b/adev/src/content/examples/aria/combobox/src/dialog/app/app.html @@ -1,14 +1,18 @@
      - arrow_drop_down +
      - search + @if (options().length === 0) { -
      No results found
      +
      No results found
      }
      @for (option of options(); track option) { -
      - {{option}} - check -
      +
      + {{ option }} + +
      }
      diff --git a/adev/src/content/examples/aria/combobox/src/dialog/material/app/app.html b/adev/src/content/examples/aria/combobox/src/dialog/material/app/app.html index 31131fafbb04..9d253a4ef0da 100644 --- a/adev/src/content/examples/aria/combobox/src/dialog/material/app/app.html +++ b/adev/src/content/examples/aria/combobox/src/dialog/material/app/app.html @@ -1,14 +1,18 @@
      - arrow_drop_down +
      - search + @if (options().length === 0) { -
      No results found
      +
      No results found
      }
      @for (option of options(); track option) { -
      - {{option}} - check -
      +
      + {{ option }} + +
      }
      diff --git a/adev/src/content/examples/aria/combobox/src/dialog/retro/app/app.html b/adev/src/content/examples/aria/combobox/src/dialog/retro/app/app.html index 4508f109511b..1f3dc3fd100c 100644 --- a/adev/src/content/examples/aria/combobox/src/dialog/retro/app/app.html +++ b/adev/src/content/examples/aria/combobox/src/dialog/retro/app/app.html @@ -1,14 +1,18 @@
      - arrow_drop_down +
      - search +
      @for (option of options(); track option) { -
      - {{option}} - check -
      +
      + {{ option }} + +
      }
      diff --git a/adev/src/content/examples/aria/grid/src/calendar/basic/app/app.html b/adev/src/content/examples/aria/grid/src/calendar/basic/app/app.html index bfe98f05ec3c..621772af2b5f 100644 --- a/adev/src/content/examples/aria/grid/src/calendar/basic/app/app.html +++ b/adev/src/content/examples/aria/grid/src/calendar/basic/app/app.html @@ -1,11 +1,15 @@
      -

      {{monthYearLabel()}}

      +

      {{ monthYearLabel() }}

      @@ -21,10 +25,10 @@

      {{monthYearLabel()}}

      @for (day of weekdays(); track day.long) { - - {{day.long}} - - + + {{ day.long }} + + } @@ -33,19 +37,21 @@

      {{monthYearLabel()}}

      @if ($first) { @for (day of daysFromPrevMonth(); track day) { - {{day}} + {{ day }} } } - + @for (day of week; track day) { - + } @if ($last && week.length < 7) { @for (day of [].constructor(7 - week.length); track $index) { - {{$index + 1}} + {{ $index + 1 }} } } diff --git a/adev/src/content/examples/aria/grid/src/calendar/material/app/app.html b/adev/src/content/examples/aria/grid/src/calendar/material/app/app.html index 464e99bffb22..255432de793a 100644 --- a/adev/src/content/examples/aria/grid/src/calendar/material/app/app.html +++ b/adev/src/content/examples/aria/grid/src/calendar/material/app/app.html @@ -1,11 +1,15 @@
      -

      {{monthYearLabel()}}

      +

      {{ monthYearLabel() }}

      @@ -21,10 +25,10 @@

      {{monthYearLabel()}}

      @for (day of weekdays(); track day.long) { - - {{day.long}} - - + + {{ day.long }} + + } @@ -33,19 +37,21 @@

      {{monthYearLabel()}}

      @if ($first) { @for (day of daysFromPrevMonth(); track day) { - {{day}} + {{ day }} } } - + @for (day of week; track day) { - + } @if ($last && week.length < 7) { @for (day of [].constructor(7 - week.length); track $index) { - {{$index + 1}} + {{ $index + 1 }} } } diff --git a/adev/src/content/examples/aria/grid/src/calendar/retro/app/app.html b/adev/src/content/examples/aria/grid/src/calendar/retro/app/app.html index 609805e43520..568d002b71d8 100644 --- a/adev/src/content/examples/aria/grid/src/calendar/retro/app/app.html +++ b/adev/src/content/examples/aria/grid/src/calendar/retro/app/app.html @@ -1,11 +1,15 @@
      -

      {{monthYearLabel()}}

      +

      {{ monthYearLabel() }}

      @@ -21,10 +25,10 @@

      {{monthYearLabel()}}

      @for (day of weekdays(); track day.long) { - - {{day.long}} - - + + {{ day.long }} + + } @@ -33,19 +37,21 @@

      {{monthYearLabel()}}

      @if ($first) { @for (day of daysFromPrevMonth(); track day) { - {{day}} + {{ day }} } } - + @for (day of week; track day) { - + } @if ($last && week.length < 7) { @for (day of [].constructor(7 - week.length); track $index) { - {{$index + 1}} + {{ $index + 1 }} } } diff --git a/adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.html b/adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.html index 693176d2c886..35e4209709ca 100644 --- a/adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.html +++ b/adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.html @@ -1,14 +1,15 @@
      @for (tag of tags(); track $index) {
      - #{{tag}} + #{{ tag }} diff --git a/adev/src/content/examples/aria/grid/src/pill-list/material/app/app.html b/adev/src/content/examples/aria/grid/src/pill-list/material/app/app.html index e117cf092d51..92b0b5ef3358 100644 --- a/adev/src/content/examples/aria/grid/src/pill-list/material/app/app.html +++ b/adev/src/content/examples/aria/grid/src/pill-list/material/app/app.html @@ -1,14 +1,15 @@
      @for (tag of tags(); track $index) {
      - {{tag}} + {{ tag }} diff --git a/adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.html b/adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.html index 304a3cd5a073..d760566a3f16 100644 --- a/adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.html +++ b/adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.html @@ -1,14 +1,15 @@
      @for (tag of tags(); track $index) {
      - #{{tag}} + #{{ tag }} diff --git a/adev/src/content/examples/aria/grid/src/table/basic/app/app.html b/adev/src/content/examples/aria/grid/src/table/basic/app/app.html index 7793e8bc2b93..b703c21eb8b4 100644 --- a/adev/src/content/examples/aria/grid/src/table/basic/app/app.html +++ b/adev/src/content/examples/aria/grid/src/table/basic/app/app.html @@ -19,8 +19,13 @@ (click)="sortTaskById()" > ID -
      + + } diff --git a/adev/src/content/examples/aria/grid/src/table/retro/app/app.html b/adev/src/content/examples/aria/grid/src/table/retro/app/app.html index 9cc31d93e472..6d073ef24dc4 100644 --- a/adev/src/content/examples/aria/grid/src/table/retro/app/app.html +++ b/adev/src/content/examples/aria/grid/src/table/retro/app/app.html @@ -19,8 +19,13 @@ (click)="sortTaskById()" > Reward -
      + + } diff --git a/adev/src/content/examples/aria/listbox/src/basic/app/app.html b/adev/src/content/examples/aria/listbox/src/basic/app/app.html index 2227728d9819..3ba1f58a884c 100644 --- a/adev/src/content/examples/aria/listbox/src/basic/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/basic/app/app.html @@ -1,10 +1,15 @@
      @for (option of options; track option) { -
      - {{ option }} - check -
      +
      + {{ option }} + +
      }
      -
      \ No newline at end of file +
      diff --git a/adev/src/content/examples/aria/listbox/src/basic/material/app/app.html b/adev/src/content/examples/aria/listbox/src/basic/material/app/app.html index 5233d4be2f66..10ed07bef1f5 100644 --- a/adev/src/content/examples/aria/listbox/src/basic/material/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/basic/material/app/app.html @@ -1,10 +1,15 @@
      @for (option of options; track option) { -
      - {{ option }} - check -
      +
      + {{ option }} + +
      }
      diff --git a/adev/src/content/examples/aria/listbox/src/basic/retro/app/app.html b/adev/src/content/examples/aria/listbox/src/basic/retro/app/app.html index 3212db18087f..658a9b614340 100644 --- a/adev/src/content/examples/aria/listbox/src/basic/retro/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/basic/retro/app/app.html @@ -1,10 +1,15 @@
      @for (option of options; track option) { -
      - {{ option }} - check -
      +
      + {{ option }} + +
      }
      diff --git a/adev/src/content/examples/aria/listbox/src/horizontal/app/app.html b/adev/src/content/examples/aria/listbox/src/horizontal/app/app.html index ceafd71c5ce3..ba7d0ea8d403 100644 --- a/adev/src/content/examples/aria/listbox/src/horizontal/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/horizontal/app/app.html @@ -1,7 +1,7 @@
      @for (amenity of amenities; track amenity) { -
      - {{ amenity }} -
      +
      + {{ amenity }} +
      }
      diff --git a/adev/src/content/examples/aria/listbox/src/horizontal/material/app/app.html b/adev/src/content/examples/aria/listbox/src/horizontal/material/app/app.html index 5a68ae1a163a..c17fa2af8891 100644 --- a/adev/src/content/examples/aria/listbox/src/horizontal/material/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/horizontal/material/app/app.html @@ -7,9 +7,11 @@ multi > @for (amenity of amenities; track amenity) { -
      - check - {{ amenity }} -
      +
      + + {{ amenity }} +
      }
      diff --git a/adev/src/content/examples/aria/listbox/src/horizontal/retro/app/app.html b/adev/src/content/examples/aria/listbox/src/horizontal/retro/app/app.html index 07639c9b9225..055f3babd2eb 100644 --- a/adev/src/content/examples/aria/listbox/src/horizontal/retro/app/app.html +++ b/adev/src/content/examples/aria/listbox/src/horizontal/retro/app/app.html @@ -1,7 +1,16 @@ -
      +
      @for (amenity of amenities; track amenity) {
      - check + {{ amenity }}
      } diff --git a/adev/src/content/examples/aria/menu/src/menu-standalone/app/app.html b/adev/src/content/examples/aria/menu/src/menu-standalone/app/app.html index 6e0c9cb28cc2..50a589e76a68 100644 --- a/adev/src/content/examples/aria/menu/src/menu-standalone/app/app.html +++ b/adev/src/content/examples/aria/menu/src/menu-standalone/app/app.html @@ -4,41 +4,57 @@
      - lock_open + Change password
      - security_key + Two-factor authentication
      - refresh + Reset - arrow_right +
      - email + Email address
      - phone + Phone number
      - vpn_key + Password
      @@ -52,12 +68,14 @@
      - help + Support
      - feedback + Feedback
      @@ -65,7 +83,7 @@
      - logout + Logout
      diff --git a/adev/src/content/examples/aria/menu/src/menu-standalone/material/app/app.html b/adev/src/content/examples/aria/menu/src/menu-standalone/material/app/app.html index fe070f3a2b3b..9ab44597c589 100644 --- a/adev/src/content/examples/aria/menu/src/menu-standalone/material/app/app.html +++ b/adev/src/content/examples/aria/menu/src/menu-standalone/material/app/app.html @@ -1,44 +1,65 @@ -
      +
      SECURITY
      - lock_open + Change password
      - security_key + Two-factor authentication
      - refresh + Reset - arrow_right +
      - email + Email address
      - phone + Phone number
      - vpn_key + Password
      @@ -52,12 +73,14 @@
      - help + Support
      - feedback + Feedback
      @@ -65,7 +88,7 @@
      - logout + Logout
      diff --git a/adev/src/content/examples/aria/menu/src/menu-standalone/retro/app/app.html b/adev/src/content/examples/aria/menu/src/menu-standalone/retro/app/app.html index 9780a2c7d421..119e2732fee7 100644 --- a/adev/src/content/examples/aria/menu/src/menu-standalone/retro/app/app.html +++ b/adev/src/content/examples/aria/menu/src/menu-standalone/retro/app/app.html @@ -1,44 +1,65 @@ -
      +
      SECURITY
      - lock_open + Change password
      - security_key + Two-factor authentication
      - refresh + Reset - arrow_right +
      - email + Email address
      - phone + Phone number
      - vpn_key + Password
      @@ -52,12 +73,14 @@
      - help + Support
      - feedback + Feedback
      @@ -65,7 +88,7 @@
      - logout + Logout
      diff --git a/adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.html b/adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.html index e24dcd20fa1c..e286d9790b2b 100644 --- a/adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.html +++ b/adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.html @@ -3,18 +3,22 @@
    • @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/disabled-focusable/retro/app/app.html b/adev/src/content/examples/aria/tree/src/disabled-focusable/retro/app/app.html index e744262f0349..8d58f5d59b6d 100644 --- a/adev/src/content/examples/aria/tree/src/disabled-focusable/retro/app/app.html +++ b/adev/src/content/examples/aria/tree/src/disabled-focusable/retro/app/app.html @@ -32,7 +32,7 @@
    -
    {{selectedCount()}} object(s) selected
    +
    {{ selectedCount() }} object(s) selected
    4.5MB
    @@ -50,13 +50,19 @@ > {{ node.name }} - + @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/multi-select/basic/app/app.html b/adev/src/content/examples/aria/tree/src/multi-select/basic/app/app.html index e59f638712ab..9de600eaa83b 100644 --- a/adev/src/content/examples/aria/tree/src/multi-select/basic/app/app.html +++ b/adev/src/content/examples/aria/tree/src/multi-select/basic/app/app.html @@ -16,18 +16,20 @@ [(expanded)]="node.expanded" #treeItem="ngTreeItem" > + + + {{ node.name }} - check - {{ node.name }} - @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/multi-select/retro/app/app.html b/adev/src/content/examples/aria/tree/src/multi-select/retro/app/app.html index f0d16d8d0e60..0ca11f38c477 100644 --- a/adev/src/content/examples/aria/tree/src/multi-select/retro/app/app.html +++ b/adev/src/content/examples/aria/tree/src/multi-select/retro/app/app.html @@ -32,7 +32,7 @@
    -
    {{selectedCount()}} object(s) selected
    +
    {{ selectedCount() }} object(s) selected
    4.5MB
    @@ -50,13 +50,19 @@ > {{ node.name }} - + @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/nav/basic/app/app.html b/adev/src/content/examples/aria/tree/src/nav/basic/app/app.html index 3a97f3fbf98d..7484a28a4eab 100644 --- a/adev/src/content/examples/aria/tree/src/nav/basic/app/app.html +++ b/adev/src/content/examples/aria/tree/src/nav/basic/app/app.html @@ -19,13 +19,17 @@ href="#{{ node.name }}" (click)="$event.preventDefault()" > - - {{ node.name }} {{ node.icon }} + {{ node.name }} + @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/single-select-follow-focus/basic/app/app.html b/adev/src/content/examples/aria/tree/src/single-select-follow-focus/basic/app/app.html index e643da55be30..b3f4ee97f26f 100644 --- a/adev/src/content/examples/aria/tree/src/single-select-follow-focus/basic/app/app.html +++ b/adev/src/content/examples/aria/tree/src/single-select-follow-focus/basic/app/app.html @@ -16,18 +16,20 @@ [(expanded)]="node.expanded" #treeItem="ngTreeItem" > + + + {{ node.name }} - check - {{ node.name }} - @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/single-select-follow-focus/retro/app/app.html b/adev/src/content/examples/aria/tree/src/single-select-follow-focus/retro/app/app.html index 9a41d8184c60..da533ea189b1 100644 --- a/adev/src/content/examples/aria/tree/src/single-select-follow-focus/retro/app/app.html +++ b/adev/src/content/examples/aria/tree/src/single-select-follow-focus/retro/app/app.html @@ -32,7 +32,7 @@
    -
    {{selectedCount()}} object(s) selected
    +
    {{ selectedCount() }} object(s) selected
    4.5MB
    @@ -50,13 +50,19 @@ > {{ node.name }} - + @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/single-select/basic/app/app.html b/adev/src/content/examples/aria/tree/src/single-select/basic/app/app.html index e21941503948..f551bcf62c3e 100644 --- a/adev/src/content/examples/aria/tree/src/single-select/basic/app/app.html +++ b/adev/src/content/examples/aria/tree/src/single-select/basic/app/app.html @@ -16,18 +16,20 @@ [(expanded)]="node.expanded" #treeItem="ngTreeItem" > + + + {{ node.name }} - check - {{ node.name }} - @if (node.children) { diff --git a/adev/src/content/examples/aria/tree/src/single-select/retro/app/app.html b/adev/src/content/examples/aria/tree/src/single-select/retro/app/app.html index e744262f0349..8d58f5d59b6d 100644 --- a/adev/src/content/examples/aria/tree/src/single-select/retro/app/app.html +++ b/adev/src/content/examples/aria/tree/src/single-select/retro/app/app.html @@ -32,7 +32,7 @@
    -
    {{selectedCount()}} object(s) selected
    +
    {{ selectedCount() }} object(s) selected
    4.5MB
    @@ -50,13 +50,19 @@ > {{ node.name }} - + @if (node.children) { diff --git a/adev/src/content/examples/attribute-directives/src/app/app.component.avoid.html b/adev/src/content/examples/attribute-directives/src/app/app.component.avoid.html index 464e8f30e56d..8abadb1242ef 100644 --- a/adev/src/content/examples/attribute-directives/src/app/app.component.avoid.html +++ b/adev/src/content/examples/attribute-directives/src/app/app.component.avoid.html @@ -1,3 +1,3 @@

    This is invalid

    - \ No newline at end of file + diff --git a/adev/src/content/examples/attribute-directives/src/app/app.component.html b/adev/src/content/examples/attribute-directives/src/app/app.component.html index 0c6c52f04167..0cdbd30cf75a 100644 --- a/adev/src/content/examples/attribute-directives/src/app/app.component.html +++ b/adev/src/content/examples/attribute-directives/src/app/app.component.html @@ -3,27 +3,25 @@

    My First Attribute Directive

    Pick a highlight color

    - Green - Yellow - Cyan + Green + Yellow + Cyan

    Highlight me!

    -

    - Highlight me too! -

    +

    Highlight me too!

    -
    +

    Mouse over the following lines to see fixed highlights

    Highlighted in yellow

    Highlighted in orange

    -
    +

    ngNonBindable

    @@ -33,6 +31,7 @@

    ngNonBindable

    ngNonBindable with a directive

    -
    This should not evaluate: {{ 1 +1 }}, but will highlight yellow. +
    + This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
    diff --git a/adev/src/content/examples/attribute-directives/src/index.html b/adev/src/content/examples/attribute-directives/src/index.html index 12cabb636261..b6d32aa42dba 100644 --- a/adev/src/content/examples/attribute-directives/src/index.html +++ b/adev/src/content/examples/attribute-directives/src/index.html @@ -1,11 +1,11 @@ - + - + Attribute Directives - - + + diff --git a/adev/src/content/examples/built-in-directives/src/app/app.component.html b/adev/src/content/examples/built-in-directives/src/app/app.component.html index 119c5c3f65e7..093f105c9a59 100644 --- a/adev/src/content/examples/built-in-directives/src/app/app.component.html +++ b/adev/src/content/examples/built-in-directives/src/app/app.component.html @@ -4,35 +4,46 @@

    Built-in attribute directives

    NgModel (two-way) Binding

    -

    NgModel examples

    +
    +

    NgModel examples

    Current item name: {{ currentItem.name }}

    - +

    - +

    - +

    -

    -

    NgClass Binding

    +
    +

    NgClass Binding

    currentClasses is {{ currentClasses | json }}

    @@ -41,35 +52,42 @@

    NgModel (two-way) Binding

    • - +
    • -
    • + +
    • - -
    • + +
    - This div should be {{ canSave ? "": "not"}} saveable, - {{ isUnchanged ? "unchanged" : "modified" }} and - {{ isSpecial ? "": "not"}} special after clicking "Refresh".
    -

    + This div should be {{ canSave ? '' : 'not' }} saveable, + {{ isUnchanged ? 'unchanged' : 'modified' }} and {{ isSpecial ? '' : 'not' }} special after + clicking "Refresh". +
    +

    This div is special
    Helpful study course
    -
    Study course
    - +
    Study course
    -

    NgStyle Binding

    -
    - This div is x-large or smaller. -
    +
    +

    NgStyle Binding

    +
    This div is x-large or smaller.

    [ngStyle] binding to currentStyles - CSS property names

    currentStyles is {{ currentStyles | json }}

    @@ -80,24 +98,28 @@

    [ngStyle] binding to currentStyles - CSS property names

    - - -
    - | - | - +
    + | + +| + -

    +

    - This div should be {{ canSave ? "italic": "plain"}}, - {{ isUnchanged ? "normal weight" : "bold" }} and, - {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".
    + This div should be {{ canSave ? 'italic' : 'plain' }}, + {{ isUnchanged ? 'normal weight' : 'bold' }} and, + {{ isSpecial ? 'extra large' : 'normal size' }} after clicking "Refresh". +
    -
    +

    Built-in structural directives

    NgIf Binding

    -

    If isActive is true, app-item-detail will render:

    +

    If isActive is true, app-item-detail will render:

    @@ -110,15 +132,16 @@

    NgIf Binding

    nullCustomer is null by default. NgIf guards against null. Give it a value to show it:

    -
    Hello, {{ nullCustomer }}
    +
    + Hello, {{ nullCustomer }} +
    -

    NgIf binding with template (no *)

    Add {{ currentItem.name }} with template -
    +

    Show/hide vs. NgIf

    @@ -129,10 +152,9 @@

    Show/hide vs. NgIf

    Show with style
    -
    Hide with style
    +
    Hide with style
    - -
    +

    NgFor Binding

    @@ -148,18 +170,17 @@

    NgFor Binding

    -

    *ngFor with index

    with semi-colon separator

    -
    {{ i + 1 }} - {{ item.name }}
    +
    {{ i + 1 }} - {{ item.name }}

    with comma separator

    -
    {{ i + 1 }} - {{ item.name }}
    +
    {{ i + 1 }} - {{ item.name }}

    *ngFor trackBy

    @@ -171,68 +192,71 @@

    *ngFor trackBy

    ({{ item.id }}) {{ item.name }}
    -
    +
    Item DOM elements change #{{ itemsNoTrackByCount }} without trackBy

    with trackBy

    -
    ({{ item.id }}) {{ item.name }}
    +
    + ({{ item.id }}) {{ item.name }} +
    Item DOM elements change #{{ itemsWithTrackByCount }} with trackBy
    -


    +


    with trackBy and semi-colon separator

    -
    - ({{ item.id }}) {{ item.name }} -
    +
    ({{ item.id }}) {{ item.name }}

    with trackBy and comma separator

    -
    ({{ item.id }}) {{ item.name }}
    +
    ({{ item.id }}) {{ item.name }}

    with trackBy and space separator

    -
    ({{ item.id }}) {{ item.name }}
    +
    ({{ item.id }}) {{ item.name }}

    with generic trackById function

    -
    ({{ item.id }}) {{ item.name }}
    +
    ({{ item.id }}) {{ item.name }}
    -

    NgSwitch Binding

    +
    +

    NgSwitch Binding

    Pick your favorite item

    -
    @@ -20,9 +21,10 @@

    Done

    cdkDropList [cdkDropListData]="done" class="example-list" - (cdkDropListDropped)="drop($event)"> + (cdkDropListDropped)="drop($event)" + > @for (item of done; track item) { -
    {{item}}
    +
    {{ item }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting-group/index.html b/adev/src/content/examples/drag-drop/src/connected-sorting-group/index.html index 720d344cf36b..5148742ac7c5 100644 --- a/adev/src/content/examples/drag-drop/src/connected-sorting-group/index.html +++ b/adev/src/content/examples/drag-drop/src/connected-sorting-group/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Connected Sorting Group Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.css b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.css index 81ffa9305f01..d1f47630d485 100644 --- a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.css @@ -13,6 +13,7 @@ border-radius: 4px; overflow: hidden; display: block; + font-family: sans-serif; } .example-box { @@ -27,6 +28,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.html b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.html index 32eda64f2b0f..578688205b9d 100755 --- a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.html @@ -7,9 +7,10 @@

    To do

    [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList]" class="example-list" - (cdkDropListDropped)="drop($event)"> + (cdkDropListDropped)="drop($event)" + > @for (item of todo; track item) { -
    {{item}}
    +
    {{ item }}
    }
    @@ -23,9 +24,10 @@

    Done

    [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList]" class="example-list" - (cdkDropListDropped)="drop($event)"> + (cdkDropListDropped)="drop($event)" + > @for (item of done; track item) { -
    {{item}}
    +
    {{ item }}
    } diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting/index.html b/adev/src/content/examples/drag-drop/src/connected-sorting/index.html index a75dd5b80c72..0898933547f8 100644 --- a/adev/src/content/examples/drag-drop/src/connected-sorting/index.html +++ b/adev/src/content/examples/drag-drop/src/connected-sorting/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Connected Sorting Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/copy-list/app/app.component.html b/adev/src/content/examples/drag-drop/src/copy-list/app/app.component.html index a3b2688685ef..eca1c3211822 100644 --- a/adev/src/content/examples/drag-drop/src/copy-list/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/copy-list/app/app.component.html @@ -10,7 +10,7 @@

    Products

    class="example-list" > @for (product of products; track $index) { -
    {{product}}
    +
    {{ product }}
    } @@ -26,7 +26,7 @@

    Shopping cart

    (cdkDropListDropped)="drop($event)" > @for (product of cart; track $index) { -
    {{product}}
    +
    {{ product }}
    } diff --git a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.css b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.css index a11481e2585f..88e48bedbe40 100644 --- a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.css @@ -13,6 +13,7 @@ border-radius: 4px; position: relative; z-index: 1; + font-family: sans-serif; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), diff --git a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.html b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.html index 368f748ea396..0ba840968710 100755 --- a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.html @@ -3,7 +3,9 @@
    - +
    diff --git a/adev/src/content/examples/drag-drop/src/custom-handle/index.html b/adev/src/content/examples/drag-drop/src/custom-handle/index.html index 5893f2bc2b89..1e359dacc1ff 100644 --- a/adev/src/content/examples/drag-drop/src/custom-handle/index.html +++ b/adev/src/content/examples/drag-drop/src/custom-handle/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Custom Handle Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.css b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.css index ff2e445b8ccc..c8ae685d5f09 100644 --- a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.css @@ -7,6 +7,7 @@ background: white; border-radius: 4px; overflow: hidden; + font-family: sans-serif; } .example-box { @@ -21,6 +22,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.html b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.html index f0673cd2abe7..fdca26c6ab69 100755 --- a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.html @@ -2,7 +2,7 @@ @for (movie of movies; track movie) {
    - {{movie}} + {{ movie }}
    } diff --git a/adev/src/content/examples/drag-drop/src/custom-placeholder/index.html b/adev/src/content/examples/drag-drop/src/custom-placeholder/index.html index dea7b854c88f..0282455f44d8 100644 --- a/adev/src/content/examples/drag-drop/src/custom-placeholder/index.html +++ b/adev/src/content/examples/drag-drop/src/custom-placeholder/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Custom Placeholder Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.css b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.css index 8e55bdbb9a65..62cc39f5cc89 100644 --- a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.css @@ -7,6 +7,7 @@ background: white; border-radius: 4px; overflow: hidden; + font-family: sans-serif; } .example-box { @@ -21,6 +22,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.html b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.html index 5072331e4076..84cc57f4c8cd 100755 --- a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.html @@ -1,8 +1,8 @@
    @for (movie of movies; track movie) {
    - {{movie.title}} - + {{ movie.title }} +
    }
    diff --git a/adev/src/content/examples/drag-drop/src/custom-preview/index.html b/adev/src/content/examples/drag-drop/src/custom-preview/index.html index 819e493a6e03..166d335aea89 100644 --- a/adev/src/content/examples/drag-drop/src/custom-preview/index.html +++ b/adev/src/content/examples/drag-drop/src/custom-preview/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Custom Preview Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.css b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.css index 1e725e0ee13c..bc7616b91eb7 100644 --- a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.css @@ -12,6 +12,7 @@ border-radius: 4px; position: relative; z-index: 1; + font-family: sans-serif; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), diff --git a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.html b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.html index 0b008e296c97..2d83f65ec994 100755 --- a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.html @@ -1,3 +1 @@ -
    - Dragging starts after one second -
    +
    Dragging starts after one second
    diff --git a/adev/src/content/examples/drag-drop/src/delay-drag/index.html b/adev/src/content/examples/drag-drop/src/delay-drag/index.html index 925f5bc09052..b1e016d87ee6 100644 --- a/adev/src/content/examples/drag-drop/src/delay-drag/index.html +++ b/adev/src/content/examples/drag-drop/src/delay-drag/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Delay Dragging Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.css b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.css index ba8046b9093e..b552395d52da 100644 --- a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.css @@ -21,6 +21,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .example-box.cdk-drag-disabled { diff --git a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.html b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.html index 75c0f30f1624..69d576bcd8c4 100755 --- a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.html @@ -1,8 +1,5 @@
    @for (item of items; track item) { -
    {{item.value}}
    +
    {{ item.value }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/disable-drag/index.html b/adev/src/content/examples/drag-drop/src/disable-drag/index.html index dbbaa5f0e767..8283d1e6324d 100644 --- a/adev/src/content/examples/drag-drop/src/disable-drag/index.html +++ b/adev/src/content/examples/drag-drop/src/disable-drag/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Disabled Dragging Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.css b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.css index 87a49006dc8d..65011080eee9 100644 --- a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.css @@ -1,7 +1,3 @@ - -HTML -TS -CSS .example-container { width: 400px; max-width: 100%; @@ -31,6 +27,7 @@ CSS cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.html b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.html index ee6b133d578b..1f2ca498db47 100755 --- a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.html @@ -7,9 +7,10 @@

    Available items

    [cdkDropListData]="items" class="example-list" cdkDropListSortingDisabled - (cdkDropListDropped)="drop($event)"> + (cdkDropListDropped)="drop($event)" + > @for (item of items; track item) { -
    {{item}}
    +
    {{ item }}
    } @@ -21,9 +22,10 @@

    Shopping basket

    cdkDropList [cdkDropListData]="basket" class="example-list" - (cdkDropListDropped)="drop($event)"> + (cdkDropListDropped)="drop($event)" + > @for (item of basket; track item) { -
    {{item}}
    +
    {{ item }}
    } diff --git a/adev/src/content/examples/drag-drop/src/disable-sorting/index.html b/adev/src/content/examples/drag-drop/src/disable-sorting/index.html index 9a01c44e88a2..a1fb3e294552 100644 --- a/adev/src/content/examples/drag-drop/src/disable-sorting/index.html +++ b/adev/src/content/examples/drag-drop/src/disable-sorting/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Disabled Sorting Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.css b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.css index 81ffa9305f01..65011080eee9 100644 --- a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.css @@ -27,6 +27,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.html b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.html index c93b307d7268..aa68caf2eed2 100755 --- a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.html @@ -8,12 +8,10 @@

    Available numbers

    cdkDropListConnectedTo="even" class="example-list" (cdkDropListDropped)="drop($event)" - [cdkDropListEnterPredicate]="noReturnPredicate"> + [cdkDropListEnterPredicate]="noReturnPredicate" + > @for (number of all; track number) { -
    {{number}}
    +
    {{ number }}
    } @@ -28,12 +26,10 @@

    Even numbers

    cdkDropListConnectedTo="all" class="example-list" (cdkDropListDropped)="drop($event)" - [cdkDropListEnterPredicate]="evenPredicate"> + [cdkDropListEnterPredicate]="evenPredicate" + > @for (number of even; track number) { -
    {{number}}
    +
    {{ number }}
    } diff --git a/adev/src/content/examples/drag-drop/src/enter-predicate/index.html b/adev/src/content/examples/drag-drop/src/enter-predicate/index.html index 0dc8589279c9..4d953949776c 100644 --- a/adev/src/content/examples/drag-drop/src/enter-predicate/index.html +++ b/adev/src/content/examples/drag-drop/src/enter-predicate/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Enter Predicate Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.css b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.css index 1e725e0ee13c..bc7616b91eb7 100644 --- a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.css @@ -12,6 +12,7 @@ border-radius: 4px; position: relative; z-index: 1; + font-family: sans-serif; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), diff --git a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.html b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.html index 2a2c74c85762..30bb84cc385b 100755 --- a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.html @@ -2,6 +2,4 @@

    -
    - Drag me around -
    +
    Drag me around
    diff --git a/adev/src/content/examples/drag-drop/src/free-drag-position/index.html b/adev/src/content/examples/drag-drop/src/free-drag-position/index.html index 6c6dac307c99..b858fdd09748 100644 --- a/adev/src/content/examples/drag-drop/src/free-drag-position/index.html +++ b/adev/src/content/examples/drag-drop/src/free-drag-position/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Free Drag Position Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.css b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.css index d093c86edf43..9029b82e9ae9 100644 --- a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.css @@ -24,6 +24,7 @@ font-size: 14px; flex-grow: 1; flex-basis: 0; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.html b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.html index f476de2fa1a2..63ad374204b0 100755 --- a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.html @@ -1,5 +1,10 @@ -
    +
    @for (timePeriod of timePeriods; track timePeriod) { -
    {{timePeriod}}
    +
    {{ timePeriod }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/horizontal-sorting/index.html b/adev/src/content/examples/drag-drop/src/horizontal-sorting/index.html index 77e729fc1b0f..4018de137ba2 100644 --- a/adev/src/content/examples/drag-drop/src/horizontal-sorting/index.html +++ b/adev/src/content/examples/drag-drop/src/horizontal-sorting/index.html @@ -1,18 +1,16 @@ - + - + Drag and Drop Horizontal Sorting Example - - + + - - + - diff --git a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.css b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.css index eec65dd76c5c..d868e7b73126 100644 --- a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.css @@ -23,6 +23,7 @@ text-align: center; font-size: 14px; min-width: 115px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.html b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.html index e081bbe4b11b..f298959715c0 100755 --- a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.html @@ -1,5 +1,10 @@ -
    +
    @for (item of items; track item) { -
    {{item}}
    +
    {{ item }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/mixed-sorting/index.html b/adev/src/content/examples/drag-drop/src/mixed-sorting/index.html index f7f8d81e85ce..13b2cf97bdbb 100644 --- a/adev/src/content/examples/drag-drop/src/mixed-sorting/index.html +++ b/adev/src/content/examples/drag-drop/src/mixed-sorting/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Horizontal Wrapping Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/overview/app/app.component.css b/adev/src/content/examples/drag-drop/src/overview/app/app.component.css index 1e725e0ee13c..bc7616b91eb7 100644 --- a/adev/src/content/examples/drag-drop/src/overview/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/overview/app/app.component.css @@ -12,6 +12,7 @@ border-radius: 4px; position: relative; z-index: 1; + font-family: sans-serif; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), diff --git a/adev/src/content/examples/drag-drop/src/overview/app/app.component.html b/adev/src/content/examples/drag-drop/src/overview/app/app.component.html index 39badfd5efb1..c02380448b97 100755 --- a/adev/src/content/examples/drag-drop/src/overview/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/overview/app/app.component.html @@ -1,3 +1 @@ -
    - Drag me around -
    +
    Drag me around
    diff --git a/adev/src/content/examples/drag-drop/src/overview/index.html b/adev/src/content/examples/drag-drop/src/overview/index.html index 75ec9ac189b6..eb32b8d7e77c 100644 --- a/adev/src/content/examples/drag-drop/src/overview/index.html +++ b/adev/src/content/examples/drag-drop/src/overview/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Overview Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/root-element/app/app.component.css b/adev/src/content/examples/drag-drop/src/root-element/app/app.component.css index b54b269f0564..f0e62e88fb99 100644 --- a/adev/src/content/examples/drag-drop/src/root-element/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/root-element/app/app.component.css @@ -9,6 +9,7 @@ align-items: center; background: #fff; border-radius: 4px; + font-family: sans-serif; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), diff --git a/adev/src/content/examples/drag-drop/src/root-element/index.html b/adev/src/content/examples/drag-drop/src/root-element/index.html index 5440efad9079..f91a550623ef 100644 --- a/adev/src/content/examples/drag-drop/src/root-element/index.html +++ b/adev/src/content/examples/drag-drop/src/root-element/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Alternate Root Element Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.css b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.css index 46153339de4c..93c1cc8e7eb6 100644 --- a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.css @@ -21,6 +21,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.html b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.html index 1e97c0c37e49..db49be41cf13 100755 --- a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.html @@ -2,11 +2,9 @@ cdkDropList class="example-list" (cdkDropListDropped)="drop($event)" - [cdkDropListSortPredicate]="sortPredicate"> + [cdkDropListSortPredicate]="sortPredicate" +> @for (number of numbers; track number) { -
    {{number}}
    +
    {{ number }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/sort-predicate/index.html b/adev/src/content/examples/drag-drop/src/sort-predicate/index.html index a7af83bc4299..38e504b1ea62 100644 --- a/adev/src/content/examples/drag-drop/src/sort-predicate/index.html +++ b/adev/src/content/examples/drag-drop/src/sort-predicate/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Sort Predicate Example - - + + - diff --git a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.css b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.css index 56f580a8e8d6..0bb2d008bb01 100644 --- a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.css +++ b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.css @@ -21,6 +21,7 @@ cursor: move; background: white; font-size: 14px; + font-family: sans-serif; } .cdk-drag-preview { diff --git a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.html b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.html index d240babbfc3d..eebe885c7e53 100755 --- a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.html +++ b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.html @@ -1,5 +1,5 @@
    @for (movie of movies; track movie) { -
    {{movie}}
    +
    {{ movie }}
    }
    diff --git a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts index b1b77d85ffd3..e3548b5646e5 100755 --- a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts +++ b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts @@ -20,7 +20,7 @@ export class CdkDragDropSortingExample { 'Episode VI - Return of the Jedi', 'Episode VII - The Force Awakens', 'Episode VIII - The Last Jedi', - 'Episode IX – The Rise of Skywalker', + 'Episode IX - The Rise of Skywalker', ]; drop(event: CdkDragDrop) { diff --git a/adev/src/content/examples/drag-drop/src/sorting/index.html b/adev/src/content/examples/drag-drop/src/sorting/index.html index 8b52a21c6d65..b086aa75ecee 100644 --- a/adev/src/content/examples/drag-drop/src/sorting/index.html +++ b/adev/src/content/examples/drag-drop/src/sorting/index.html @@ -1,17 +1,16 @@ - + - + Drag and Drop Sorting Example - - + + - diff --git a/adev/src/content/examples/dynamic-form/src/index.html b/adev/src/content/examples/dynamic-form/src/index.html index 20688f24057e..5e9e52a862b9 100644 --- a/adev/src/content/examples/dynamic-form/src/index.html +++ b/adev/src/content/examples/dynamic-form/src/index.html @@ -1,14 +1,13 @@ - + - - + + Dynamic Form - - + + - diff --git a/adev/src/content/examples/elements/src/index.html b/adev/src/content/examples/elements/src/index.html index e8a1b6e353d1..37a52162d764 100644 --- a/adev/src/content/examples/elements/src/index.html +++ b/adev/src/content/examples/elements/src/index.html @@ -1,10 +1,10 @@ - + - - + + Elements - + diff --git a/adev/src/content/examples/form-validation/src/app/app.component.ts b/adev/src/content/examples/form-validation/src/app/app.component.ts index ec60c3caa014..267aaaa879d4 100644 --- a/adev/src/content/examples/form-validation/src/app/app.component.ts +++ b/adev/src/content/examples/form-validation/src/app/app.component.ts @@ -8,9 +8,9 @@ import {ActorFormTemplateComponent} from './template/actor-form-template.compone @Component({ selector: 'app-root', template: `

    Form validation example

    - -
    - `, + +
    + `, imports: [ ActorFormReactiveComponent, FormsModule, diff --git a/adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html b/adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html index 4cac0fe43020..7960e7180fbd 100644 --- a/adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html +++ b/adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html @@ -1,38 +1,30 @@
    -

    Reactive Form

    -
    - -
    +
    - - + @if (name.invalid && (name.dirty || name.touched)) {
    - @if (name.hasError('required')) { -
    - Name is required. -
    +
    Name is required.
    } @if (name.hasError('minlength')) { -
    - Name must be at least 4 characters long. -
    +
    Name must be at least 4 characters long.
    } @if (name.hasError('forbiddenName')) { -
    - Name cannot be Bob. -
    +
    Name cannot be Bob.
    }
    } @@ -41,8 +33,7 @@

    Reactive Form

    - + @if (role.pending) {
    Validating...
    @@ -50,9 +41,7 @@

    Reactive Form

    @if (role.invalid) {
    @if (role.hasError('uniqueRole')) { -
    - Role is already taken. -
    +
    Role is already taken.
    }
    } @@ -69,8 +58,7 @@

    Reactive Form

    - @for (skill of skills; track $index) { } @@ -86,13 +74,8 @@

    Reactive Form

    Complete the form to enable the Submit button.

    - - + +
    diff --git a/adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html b/adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html index 0ffcf994c99c..1e3f93b8ff16 100644 --- a/adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html +++ b/adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html @@ -1,41 +1,44 @@
    -

    Template-Driven Form

    -
    +
    - + @if (name.invalid && (name.dirty || name.touched)) {
    - @if (name.hasError('required')) { -
    - Name is required. -
    +
    Name is required.
    } @if (name.hasError('minlength')) { -
    - Name must be at least 4 characters long. -
    +
    Name must be at least 4 characters long.
    } @if (name.hasError('forbiddenName')) { -
    - Name cannot be Bob. -
    +
    Name cannot be Bob.
    } -
    } @@ -44,13 +47,15 @@

    Template-Driven Form

    - + @if (role.pending) {
    Validating...
    @@ -58,9 +63,7 @@

    Template-Driven Form

    @if (role.invalid) {
    @if (role.hasError('uniqueRole')) { -
    - Role is already taken. -
    +
    Role is already taken.
    }
    } @@ -68,19 +71,14 @@

    Template-Driven Form

    @if (actorForm.hasError('unambiguousRole') && (actorForm.touched || actorForm.dirty)) { -
    - Name cannot match role. -
    +
    Name cannot match role.
    }
    - @for (skill of skills; track $index) { } @@ -96,12 +94,8 @@

    Template-Driven Form

    Complete the form to enable the Submit button.

    - - + +
    @if (actorForm.submitted) { diff --git a/adev/src/content/examples/form-validation/src/index.html b/adev/src/content/examples/form-validation/src/index.html index d4e9dbee5773..18aab355cf3f 100644 --- a/adev/src/content/examples/form-validation/src/index.html +++ b/adev/src/content/examples/form-validation/src/index.html @@ -1,15 +1,14 @@ - + Hero Form with Validation - - - - + + + + - diff --git a/adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts b/adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts index 87d100917f40..85f0e2f48d75 100644 --- a/adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts +++ b/adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts @@ -3,9 +3,7 @@ import {FormControl, ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'app-reactive-favorite-color', - template: ` - Favorite Color: - `, + template: ` Favorite Color: `, imports: [ReactiveFormsModule], }) export class FavoriteColorReactiveComponent { diff --git a/adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.ts b/adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.ts index afcd75e5be13..f628567b9eb3 100644 --- a/adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.ts +++ b/adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.ts @@ -3,9 +3,7 @@ import {FormsModule} from '@angular/forms'; @Component({ selector: 'app-template-favorite-color', - template: ` - Favorite Color: - `, + template: ` Favorite Color: `, imports: [FormsModule], }) export class FavoriteColorTemplateComponent { diff --git a/adev/src/content/examples/forms-overview/src/index.html b/adev/src/content/examples/forms-overview/src/index.html index 988ae05585ce..8e49fb042e77 100644 --- a/adev/src/content/examples/forms-overview/src/index.html +++ b/adev/src/content/examples/forms-overview/src/index.html @@ -1,13 +1,13 @@ - + - - - Forms Overview - - - - - - - + + + Forms Overview + + + + + + + diff --git a/adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html b/adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html index 6eea779b5a11..7119692e8d6c 100644 --- a/adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html +++ b/adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html @@ -10,13 +10,18 @@

    Actor Form

    - + -
    - +
    + Name is required
    @@ -24,15 +29,25 @@

    Actor Form

    - +
    - @for (skill of skills; track $index) { } @@ -43,30 +58,29 @@

    Actor Form

    - + - + - - with reset - -    + + with reset    - + without reset - +
    -
    +
    Name via form.controls = {{ showFormControls(actorForm) }}
    - - + +
    @@ -85,117 +99,123 @@

    You submitted the following:

    Skill
    {{ model.skill }}
    -
    - +
    +
    - + -
    -
    - +
    + + - - - -
    + + +
    -
    +
    -

    Actor Form

    -
    -
    - - -
    - -
    - - -
    +

    Actor Form

    + +
    + + +
    - -
    - - -
    - - +
    + + +
    -
    + +
    + + +
    + + +
    -
    +
    -

    Actor Form

    - -
    +

    Actor Form

    + + - - {{ model | json }} -
    - - -
    - -
    - - -
    + + {{ model | json }} +
    + + +
    -
    - - -
    +
    + + +
    - - +
    + + +
    -
    + + +
    -
    +
    - - TODO: remove this: {{ model.name}} + + TODO: remove this: {{ model.name }} -
    - - TODO: remove this: {{ model.name}} +
    + + TODO: remove this: {{ model.name }}
    diff --git a/adev/src/content/examples/forms/src/index.html b/adev/src/content/examples/forms/src/index.html index be443ca0da10..333d72d527f2 100644 --- a/adev/src/content/examples/forms/src/index.html +++ b/adev/src/content/examples/forms/src/index.html @@ -1,21 +1,19 @@ - + Hero Form - - - + + + - - - + + + - diff --git a/adev/src/content/examples/hello-world/src/index.html b/adev/src/content/examples/hello-world/src/index.html index 7cbb82dfb096..41a869c023f7 100644 --- a/adev/src/content/examples/hello-world/src/index.html +++ b/adev/src/content/examples/hello-world/src/index.html @@ -1,14 +1,13 @@ - + - - + + Hello World - - + + - diff --git a/adev/src/content/examples/i18n/doc-files/app.component.html b/adev/src/content/examples/i18n/doc-files/app.component.html index 1e5da1744cc0..b5b7afeffbf5 100644 --- a/adev/src/content/examples/i18n/doc-files/app.component.html +++ b/adev/src/content/examples/i18n/doc-files/app.component.html @@ -27,7 +27,7 @@

    Hello i18n!

    -Angular logo +Angular logo diff --git a/adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html b/adev/src/content/examples/i18n/doc-files/messages.fr.xlf similarity index 100% rename from adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html rename to adev/src/content/examples/i18n/doc-files/messages.fr.xlf diff --git a/adev/src/content/examples/i18n/doc-files/rendered-output.html b/adev/src/content/examples/i18n/doc-files/rendered-output.html index 139a7e7f361e..dbe2a8f7a12f 100644 --- a/adev/src/content/examples/i18n/doc-files/rendered-output.html +++ b/adev/src/content/examples/i18n/doc-files/rendered-output.html @@ -1,3 +1,3 @@

    Bonjour

    -

    Bonjour

    \ No newline at end of file +

    Bonjour

    diff --git a/adev/src/content/examples/i18n/src/app/app.component.html b/adev/src/content/examples/i18n/src/app/app.component.html index 2e2509a64ba8..ece73cd5a873 100644 --- a/adev/src/content/examples/i18n/src/app/app.component.html +++ b/adev/src/content/examples/i18n/src/app/app.component.html @@ -1,7 +1,5 @@ -

    - Hello i18n! -

    +

    Hello i18n!

    I don't output any element @@ -10,31 +8,36 @@

    +Angular logo -
    +
    -Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{ minutes }} minutes ago}} +Updated + {minutes, plural, =0 {just now} =1 {one minute ago} other {{{ minutes }} minutes ago}} -({{ minutes }}) -

    +({{ minutes }})

    The author is {gender, select, male {male} female {female} other {other}} -

    +

    -Updated: {minutes, plural, - =0 {just now} - =1 {one minute ago} - other {{{ minutes }} minutes ago by {gender, select, male {male} female {female} other {other}}}} +Updated: + {minutes, plural, + =0 {just now} + =1 {one minute ago} + other {{{ minutes }} minutes ago by {gender, select, male {male} female {female} other {other}}} + } -

    +

    -
    {{toggle()}}
    +
    {{ toggle() }}
    diff --git a/adev/src/content/examples/i18n/src/index.html b/adev/src/content/examples/i18n/src/index.html index 328627d72ec2..bc556456739c 100644 --- a/adev/src/content/examples/i18n/src/index.html +++ b/adev/src/content/examples/i18n/src/index.html @@ -1,11 +1,11 @@ - + - + Angular i18n example - - + + Loading... diff --git a/adev/src/content/examples/pipes/src/app/app.component.html b/adev/src/content/examples/pipes/src/app/app.component.html index cccf30cf850f..8f707596f7a4 100644 --- a/adev/src/content/examples/pipes/src/app/app.component.html +++ b/adev/src/content/examples/pipes/src/app/app.component.html @@ -9,28 +9,28 @@

    Pipes

    Flying Heroes filter pipe (pure) Flying Heroes filter pipe (impure) -
    +

    Date Pipe

    -
    +

    Date Pipe Formatting

    -
    +

    Pipe Chaining

    -
    +

    Pipes and Precedence

    -
    +
    -
    +

    Json Pipe for Debugging

    Use the JsonPipe to display component properties for debugging.

    data | json @@ -38,8 +38,8 @@

    Json Pipe for Debugging

    -
    +
    -
    +
    diff --git a/adev/src/content/examples/pipes/src/app/birthday-formatting.component.html b/adev/src/content/examples/pipes/src/app/birthday-formatting.component.html index 7bb26ed72566..d7fe15157e0c 100644 --- a/adev/src/content/examples/pipes/src/app/birthday-formatting.component.html +++ b/adev/src/content/examples/pipes/src/app/birthday-formatting.component.html @@ -1,6 +1,8 @@ -

    The hero's birthday is {{ birthday | date:"shortDate" }} in the "shortDate" format.

    +

    The hero's birthday is {{ birthday | date: 'shortDate' }} in the "shortDate" format.

    -

    The hero's birthday is {{ birthday | date:format }} in "{{ format }}" format. - Click the toggle button to change formats.

    +

    + The hero's birthday is {{ birthday | date: format }} in "{{ format }}" format. Click the toggle + button to change formats. +

    diff --git a/adev/src/content/examples/pipes/src/app/birthday-pipe-chaining.component.html b/adev/src/content/examples/pipes/src/app/birthday-pipe-chaining.component.html index b4af52ad6b79..97483e9a1dc3 100644 --- a/adev/src/content/examples/pipes/src/app/birthday-pipe-chaining.component.html +++ b/adev/src/content/examples/pipes/src/app/birthday-pipe-chaining.component.html @@ -5,5 +5,5 @@

    The chained hero's uppercase birthday in "fullDate" format is - {{ birthday | date:'fullDate' | uppercase }} + {{ birthday | date: 'fullDate' | uppercase }}

    diff --git a/adev/src/content/examples/pipes/src/app/flying-heroes-impure.component.html b/adev/src/content/examples/pipes/src/app/flying-heroes-impure.component.html index 9a94d86e3b9a..7202ee9b45e1 100644 --- a/adev/src/content/examples/pipes/src/app/flying-heroes-impure.component.html +++ b/adev/src/content/examples/pipes/src/app/flying-heroes-impure.component.html @@ -3,21 +3,27 @@

    {{ title }}

    - +
    - +
    - Mutate array + Mutate array

    Heroes who fly (piped)

    - @for (hero of (heroes | flyingHeroesImpure); track hero) { + @for (hero of heroes | flyingHeroesImpure; track hero) {
    {{ hero.name }}
    } diff --git a/adev/src/content/examples/pipes/src/app/flying-heroes.component.html b/adev/src/content/examples/pipes/src/app/flying-heroes.component.html index 979578f56609..98afe701c544 100644 --- a/adev/src/content/examples/pipes/src/app/flying-heroes.component.html +++ b/adev/src/content/examples/pipes/src/app/flying-heroes.component.html @@ -1,27 +1,33 @@

    {{ title }}

    -

    Create a new hero and press enter to add it to the list.

    +

    Create a new hero and press enter to add it to the list.

    - +
    - +
    - + - +

    Heroes who fly (piped)

    - @for (hero of (heroes | flyingHeroes); track hero) { + @for (hero of heroes | flyingHeroes; track hero) {
    {{ hero.name }}
    } diff --git a/adev/src/content/examples/pipes/src/app/flying-heroes.component.ts b/adev/src/content/examples/pipes/src/app/flying-heroes.component.ts index 2abba0ffab84..0796b7709e8b 100644 --- a/adev/src/content/examples/pipes/src/app/flying-heroes.component.ts +++ b/adev/src/content/examples/pipes/src/app/flying-heroes.component.ts @@ -13,10 +13,17 @@ import {HEROES} from './heroes'; imports: [CommonModule, FormsModule, FlyingHeroesPipe], styles: [ ` - #flyers, #all {font-style: italic} - button {display: block} - input {margin: .25rem .25rem .5rem 0;} - `, + #flyers, + #all { + font-style: italic; + } + button { + display: block; + } + input { + margin: 0.25rem 0.25rem 0.5rem 0; + } + `, ], }) // #docregion v1 diff --git a/adev/src/content/examples/pipes/src/app/hero-async-message.component.ts b/adev/src/content/examples/pipes/src/app/hero-async-message.component.ts index c5d7f8ffd559..72de7c50ae70 100644 --- a/adev/src/content/examples/pipes/src/app/hero-async-message.component.ts +++ b/adev/src/content/examples/pipes/src/app/hero-async-message.component.ts @@ -6,8 +6,7 @@ import {map, startWith, take} from 'rxjs/operators'; @Component({ selector: 'app-hero-async-message', - template: ` -

    Async Messages and AsyncPipe

    + template: `

    Async Messages and AsyncPipe

    {{ message$ | async }}

    `, imports: [AsyncPipe], diff --git a/adev/src/content/examples/pipes/src/app/power-booster.component.ts b/adev/src/content/examples/pipes/src/app/power-booster.component.ts index 73d9839c74c3..1c70e3ed52b7 100644 --- a/adev/src/content/examples/pipes/src/app/power-booster.component.ts +++ b/adev/src/content/examples/pipes/src/app/power-booster.component.ts @@ -5,7 +5,7 @@ import {ExponentialStrengthPipe} from './exponential-strength.pipe'; selector: 'app-power-booster', template: `

    Power Booster

    -

    Super power boost: {{2 | exponentialStrength: 10}}

    +

    Super power boost: {{ 2 | exponentialStrength: 10 }}

    `, imports: [ExponentialStrengthPipe], }) diff --git a/adev/src/content/examples/pipes/src/app/precedence.component.html b/adev/src/content/examples/pipes/src/app/precedence.component.html index 2c9b8331175c..da4265287abc 100644 --- a/adev/src/content/examples/pipes/src/app/precedence.component.html +++ b/adev/src/content/examples/pipes/src/app/precedence.component.html @@ -1,7 +1,8 @@

    - In most cases, you'll wrap the entire ternary expression in parentheses before passing the result to a pipe. + In most cases, you'll wrap the entire ternary expression in parentheses before passing the result + to a pipe.

    @@ -12,27 +13,30 @@

    -

    Without parentheses, only the second value is uppercased.

    -

    Example: isGood ? 'good' : 'bad' | uppercase +

    + Example: isGood ? 'good' : 'bad' | uppercase - {{ isGood ? 'good' : 'bad' | uppercase }} + {{ isGood ? 'good' : ('bad' | uppercase) }}

    -

    Same as: isGood ? 'good' : ('bad' | uppercase) +

    + Same as: isGood ? 'good' : ('bad' | uppercase) {{ isGood ? 'good' : ('bad' | uppercase) }}

    +

    + If only one of the values should be passed to a pipe, be explicit and surround that value with + parentheses. +

    -

    If only one of the values should be passed to a pipe, - be explicit and surround that value with parentheses.

    - -

    Example: isUpper ? ('upper' | uppercase) : 'lower' +

    + Example: isUpper ? ('upper' | uppercase) : 'lower' {{ isUpper ? ('upper' | uppercase) : 'lower' }} diff --git a/adev/src/content/examples/pipes/src/index.html b/adev/src/content/examples/pipes/src/index.html index 350026389594..79c649b2e7a8 100644 --- a/adev/src/content/examples/pipes/src/index.html +++ b/adev/src/content/examples/pipes/src/index.html @@ -1,14 +1,13 @@ - + Pipes - - - + + + - diff --git a/adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html b/adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html index 0bf3482d4dc9..a9206184ee8d 100644 --- a/adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html +++ b/adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html @@ -1,6 +1,6 @@ - + diff --git a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html index cc00ded43178..8b5cea04be10 100644 --- a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html +++ b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html @@ -30,12 +30,12 @@

    Address

    Aliases

    - @for(alias of aliases.controls; track $index; let i = $index) { -
    - - - -
    + @for (alias of aliases.controls; track $index; let i = $index) { +
    + + + +
    }
    diff --git a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html index bc37bc682143..46e664a7ee23 100644 --- a/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html +++ b/adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html @@ -45,7 +45,7 @@

    Aliases

    -
    +

    Form Value: {{ profileForm.value | json }}

    diff --git a/adev/src/content/examples/reactive-forms/src/index.html b/adev/src/content/examples/reactive-forms/src/index.html index 4a6b8fdac082..3009c261c17b 100644 --- a/adev/src/content/examples/reactive-forms/src/index.html +++ b/adev/src/content/examples/reactive-forms/src/index.html @@ -1,14 +1,13 @@ - + Angular Reactive Forms - - + + - diff --git a/adev/src/content/examples/resolution-modifiers/src/app/host-child/host-child.component.html b/adev/src/content/examples/resolution-modifiers/src/app/host-child/host-child.component.html index a2c67ab1e17c..d21f41c95205 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/host-child/host-child.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/host-child/host-child.component.html @@ -1,4 +1,4 @@
    -

    Child of @Host() Component

    +

    Child of @Host() Component

    Flower emoji: {{ flower.emoji }}

    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/host-parent/host-parent.component.html b/adev/src/content/examples/resolution-modifiers/src/app/host-parent/host-parent.component.html index 9aae417cb947..129d5b77aebd 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/host-parent/host-parent.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/host-parent/host-parent.component.html @@ -1,5 +1,5 @@
    -

    Parent of @Host() Component

    -

    Flower emoji: {{ flower.emoji }}

    - +

    Parent of @Host() Component

    +

    Flower emoji: {{ flower.emoji }}

    +
    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/host/host.component.html b/adev/src/content/examples/resolution-modifiers/src/app/host/host.component.html index b0ce20a2c035..1f17295fe8d1 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/host/host.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/host/host.component.html @@ -1,6 +1,6 @@
    -

    @Host() Component

    -

    Flower emoji: {{ flower?.emoji }}

    -

    (@Host() stops it here)

    - +

    @Host() Component

    +

    Flower emoji: {{ flower?.emoji }}

    +

    (@Host() stops it here)

    +
    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/optional/optional.component.html b/adev/src/content/examples/resolution-modifiers/src/app/optional/optional.component.html index e73b2bc67b92..e5017994904c 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/optional/optional.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/optional/optional.component.html @@ -1,4 +1,8 @@
    -

    @Optional() Component

    -

    This component still works even though the OptionalService (notice @Optional() in the constructor isn't provided or configured anywhere. Angular goes through tree and visibility rules, and if it doesn't find the requested service, returns null.

    +

    @Optional() Component

    +

    + This component still works even though the OptionalService (notice @Optional() in the + constructor isn't provided or configured anywhere. Angular goes through tree and visibility + rules, and if it doesn't find the requested service, returns null. +

    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/self-no-data/self-no-data.component.html b/adev/src/content/examples/resolution-modifiers/src/app/self-no-data/self-no-data.component.html index a0d9c3b3c0fa..84d6306da3f9 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/self-no-data/self-no-data.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/self-no-data/self-no-data.component.html @@ -1,4 +1,4 @@
    -

    @Self() Component (without a provider)

    +

    @Self() Component (without a provider)

    Leaf emoji: {{ leaf?.emoji }}

    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/self/self.component.html b/adev/src/content/examples/resolution-modifiers/src/app/self/self.component.html index 150900c6ad27..fba7c08ed70b 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/self/self.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/self/self.component.html @@ -1,4 +1,4 @@
    -

    @Self() Component

    +

    @Self() Component

    Flower emoji: {{ flower.emoji }}

    diff --git a/adev/src/content/examples/resolution-modifiers/src/app/skipself/skipself.component.html b/adev/src/content/examples/resolution-modifiers/src/app/skipself/skipself.component.html index 3861d4513843..01bc38abdeb7 100755 --- a/adev/src/content/examples/resolution-modifiers/src/app/skipself/skipself.component.html +++ b/adev/src/content/examples/resolution-modifiers/src/app/skipself/skipself.component.html @@ -1,4 +1,4 @@
    -

    @SkipSelf() Component

    +

    @SkipSelf() Component

    Leaf emoji: {{ leaf.emoji }}

    diff --git a/adev/src/content/examples/resolution-modifiers/src/index.html b/adev/src/content/examples/resolution-modifiers/src/index.html index 4ec849ae7c01..846c8514a42c 100644 --- a/adev/src/content/examples/resolution-modifiers/src/index.html +++ b/adev/src/content/examples/resolution-modifiers/src/index.html @@ -1,13 +1,13 @@ - + - - - DI Resolution Modifiers Example - - - - - - Loading... - + + + DI Resolution Modifiers Example + + + + + + Loading... + diff --git a/adev/src/content/examples/router-tutorial/src/app/app.component.html b/adev/src/content/examples/router-tutorial/src/app/app.component.html index fa2cf35fb8ea..80ae3a9a46da 100644 --- a/adev/src/content/examples/router-tutorial/src/app/app.component.html +++ b/adev/src/content/examples/router-tutorial/src/app/app.component.html @@ -4,16 +4,21 @@

    Angular Router Sample

    @@ -22,20 +27,18 @@

    Angular Router Sample

    -
    - - - - - - - - - - - +
    + + + + + + + + +
    diff --git a/adev/src/content/examples/router-tutorial/src/index.html b/adev/src/content/examples/router-tutorial/src/index.html index edfa60e4c972..a5967ee545d6 100644 --- a/adev/src/content/examples/router-tutorial/src/index.html +++ b/adev/src/content/examples/router-tutorial/src/index.html @@ -1,13 +1,13 @@ - + - - - Angular Router Sample - - - - - - - + + + Angular Router Sample + + + + + + + diff --git a/adev/src/content/examples/router/src/app/app.component.1.html b/adev/src/content/examples/router/src/app/app.component.1.html index 6a5611dff0e6..925bb944cef5 100644 --- a/adev/src/content/examples/router/src/app/app.component.1.html +++ b/adev/src/content/examples/router/src/app/app.component.1.html @@ -1,7 +1,9 @@

    Angular Router

    diff --git a/adev/src/content/examples/router/src/index.html b/adev/src/content/examples/router/src/index.html index edb919d1ec97..51b7f07c498c 100644 --- a/adev/src/content/examples/router/src/index.html +++ b/adev/src/content/examples/router/src/index.html @@ -1,19 +1,18 @@ - + - + Angular Router - - + + - diff --git a/adev/src/content/examples/routing-with-urlmatcher/src/app/profile/profile.component.html b/adev/src/content/examples/routing-with-urlmatcher/src/app/profile/profile.component.html index 4de1b0058c3e..c7bd65f59e52 100644 --- a/adev/src/content/examples/routing-with-urlmatcher/src/app/profile/profile.component.html +++ b/adev/src/content/examples/routing-with-urlmatcher/src/app/profile/profile.component.html @@ -1,3 +1 @@ -

    - Hello {{ username() }}! -

    \ No newline at end of file +

    Hello {{ username() }}!

    diff --git a/adev/src/content/examples/routing-with-urlmatcher/src/index.html b/adev/src/content/examples/routing-with-urlmatcher/src/index.html index 8117be96b328..7e16a6df2240 100644 --- a/adev/src/content/examples/routing-with-urlmatcher/src/index.html +++ b/adev/src/content/examples/routing-with-urlmatcher/src/index.html @@ -1,10 +1,10 @@ - + - + Angular App - loading + loading diff --git a/adev/src/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.ts b/adev/src/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.ts index 6a5d214a4339..619191fe4019 100644 --- a/adev/src/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.ts +++ b/adev/src/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.ts @@ -2,11 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'lib-my-lib', - template: ` -

    - my-lib works! -

    - `, + template: `

    my-lib works!

    `, styles: [], standalone: false, }) diff --git a/adev/src/content/examples/schematics-for-libraries/src/app/app.component.ts b/adev/src/content/examples/schematics-for-libraries/src/app/app.component.ts index 5a7691322272..fe9a1ab548a7 100644 --- a/adev/src/content/examples/schematics-for-libraries/src/app/app.component.ts +++ b/adev/src/content/examples/schematics-for-libraries/src/app/app.component.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` -

    Library Schematics

    - `, + template: `

    Library Schematics

    `, styles: [], standalone: false, }) diff --git a/adev/src/content/examples/schematics-for-libraries/src/index.html b/adev/src/content/examples/schematics-for-libraries/src/index.html index e2eadbd1aed5..b1d4fbabc46d 100644 --- a/adev/src/content/examples/schematics-for-libraries/src/index.html +++ b/adev/src/content/examples/schematics-for-libraries/src/index.html @@ -1,13 +1,13 @@ - + - - - SchematicsForLibraries - - - - - - - + + + SchematicsForLibraries + + + + + + + diff --git a/adev/src/content/examples/security/src/app/bypass-security.component.html b/adev/src/content/examples/security/src/app/bypass-security.component.html index c76f57705a46..62b348065f5d 100644 --- a/adev/src/content/examples/security/src/app/bypass-security.component.html +++ b/adev/src/content/examples/security/src/app/bypass-security.component.html @@ -12,6 +12,18 @@

    A trusted URL:

    Resource URL:

    Showing: {{ dangerousVideoUrl }}

    Trusted:

    - +

    Untrusted:

    - + diff --git a/adev/src/content/examples/security/src/index.html b/adev/src/content/examples/security/src/index.html index 4cf1cd642855..37bf26556a7b 100644 --- a/adev/src/content/examples/security/src/index.html +++ b/adev/src/content/examples/security/src/index.html @@ -1,11 +1,11 @@ - + Angular Content Security - - - + + + diff --git a/adev/src/content/examples/service-worker-getting-started/src/app/app.component.html b/adev/src/content/examples/service-worker-getting-started/src/app/app.component.html index 462335339c1e..13ba71b60bca 100755 --- a/adev/src/content/examples/service-worker-getting-started/src/app/app.component.html +++ b/adev/src/content/examples/service-worker-getting-started/src/app/app.component.html @@ -1,21 +1,31 @@ -
    -

    - Welcome to {{ title }}! -

    - Angular Logo +
    +

    Welcome to {{ title }}!

    + Angular Logo

    {{ updateCheckText }}

    -

    Here are some links to help you start:

    +

    Here are some links to help you start:

    • -

      Angular Service Worker Intro

      +

      + Angular Service Worker Intro +

    • -

      CLI Documentation

      +

      + CLI Documentation +

    • Angular blog

      diff --git a/adev/src/content/examples/service-worker-getting-started/src/index.html b/adev/src/content/examples/service-worker-getting-started/src/index.html index 9b548d69464f..3427951e1945 100755 --- a/adev/src/content/examples/service-worker-getting-started/src/index.html +++ b/adev/src/content/examples/service-worker-getting-started/src/index.html @@ -1,12 +1,11 @@ - + - - - SwExample - - - - - - + + + SwExample + + + + + diff --git a/adev/src/content/examples/signal-forms/src/comparison/app/reactive-forms.ts b/adev/src/content/examples/signal-forms/src/comparison/app/reactive-forms.ts index 105e5fbae4ea..6957c3c6affc 100644 --- a/adev/src/content/examples/signal-forms/src/comparison/app/reactive-forms.ts +++ b/adev/src/content/examples/signal-forms/src/comparison/app/reactive-forms.ts @@ -40,9 +40,7 @@ import {FormGroup, FormControl, Validators, ReactiveFormsModule} from '@angular/ }
    - + `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/src/content/examples/signal-forms/src/comparison/app/signal-forms.ts b/adev/src/content/examples/signal-forms/src/comparison/app/signal-forms.ts index c2ff0a82ef95..2a73e24c45d5 100644 --- a/adev/src/content/examples/signal-forms/src/comparison/app/signal-forms.ts +++ b/adev/src/content/examples/signal-forms/src/comparison/app/signal-forms.ts @@ -30,9 +30,7 @@ import {form, Field, required, email, minLength} from '@angular/forms/signals'; }
    - + `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/src/content/examples/signal-forms/src/comparison/app/template-driven-forms.ts b/adev/src/content/examples/signal-forms/src/comparison/app/template-driven-forms.ts index 978855f0807b..be3b09fe3796 100644 --- a/adev/src/content/examples/signal-forms/src/comparison/app/template-driven-forms.ts +++ b/adev/src/content/examples/signal-forms/src/comparison/app/template-driven-forms.ts @@ -54,9 +54,7 @@ import {FormsModule} from '@angular/forms'; }
    - + `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/src/content/examples/structural-directives/src/app/app.component.html b/adev/src/content/examples/structural-directives/src/app/app.component.html index b609e6bb1d6e..1e521ad0a990 100644 --- a/adev/src/content/examples/structural-directives/src/app/app.component.html +++ b/adev/src/content/examples/structural-directives/src/app/app.component.html @@ -5,48 +5,38 @@

    Structural Directives

    Conditional display of hero

    - -
    {{hero.name}}
    - + +
    {{ hero.name }}
    +

    List of heroes

      -
    • {{hero.name}}
    • +
    • {{ hero.name }}
    -
    +

    NgIf

    -

    - Expression is true and ngIf is true. - This paragraph is in the DOM. -

    -

    - Expression is false and ngIf is false. - This paragraph is not in the DOM. -

    +

    Expression is true and ngIf is true. This paragraph is in the DOM.

    +

    Expression is false and ngIf is false. This paragraph is not in the DOM.

    -

    - Expression sets display to "block". - This paragraph is visible. -

    +

    Expression sets display to "block". This paragraph is visible.

    - Expression sets display to "none". - This paragraph is hidden but still in the DOM. + Expression sets display to "none". This paragraph is hidden but still in the DOM.

    NgIf with template

    <ng-template> element

    -
    {{hero.name}}
    +
    {{ hero.name }}
    -
    +

    <ng-container>

    @@ -57,31 +47,28 @@

    *ngIf with a <ng-container>

    I turned the corner - - and saw {{hero.name}}. I waved - + and saw {{ hero.name }}. I waved and continued on my way.

    I turned the corner - - and saw {{hero.name}}. I waved - + and saw {{ hero.name }}. I waved and continued on my way.

    <select> with <span>

    - Pick your favorite hero - () + Pick your favorite hero ()
    @@ -89,64 +76,79 @@

    *ngIf with a <ng-container>

    <select> with <ng-container>

    - Pick your favorite hero - () + Pick your favorite hero ()
    -

    +

    -
    +

    NgFor

    - -

    <div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">

    - -
    - ({{i}}) {{hero.name}} -
    - - -

    <ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/>

    - - -
    - ({{i}}) {{hero.name}} +

    + <div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" + [class.odd]="odd"> +

    + +
    + ({{ i }}) {{ hero.name }}
    - - + +

    + <ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" + [ngForTrackBy]="trackById"/> +

    + + +
    ({{ i }}) {{ hero.name }}
    +
    +
    -
    +

    NgSwitch

    Pick your favorite hero

    -

    NgSwitch

    - - + + - +

    NgSwitch with <ng-template>

    @@ -159,20 +161,20 @@

    NgSwitch with <ng-template>

    - +
    -
    +
    -
    +

    IfLoadedDirective

    -
    +

    TrigonometryDirective

    diff --git a/adev/src/content/examples/structural-directives/src/index.html b/adev/src/content/examples/structural-directives/src/index.html index 451b66ce357d..653c268a6c21 100644 --- a/adev/src/content/examples/structural-directives/src/index.html +++ b/adev/src/content/examples/structural-directives/src/index.html @@ -1,15 +1,14 @@ - + Angular Structural Directives - - - + + + - diff --git a/adev/src/content/examples/testing/src/app/dashboard/dashboard.component.html b/adev/src/content/examples/testing/src/app/dashboard/dashboard.component.html index 250b33bac7f4..83b20316559c 100644 --- a/adev/src/content/examples/testing/src/app/dashboard/dashboard.component.html +++ b/adev/src/content/examples/testing/src/app/dashboard/dashboard.component.html @@ -3,12 +3,7 @@

    {{ title }}

    @for (hero of heroes; track hero) { - - + }
    diff --git a/adev/src/content/examples/testing/src/app/demo/demo.ts b/adev/src/content/examples/testing/src/app/demo/demo.ts index 81b49880f8cc..e78dcce2eb3f 100755 --- a/adev/src/content/examples/testing/src/app/demo/demo.ts +++ b/adev/src/content/examples/testing/src/app/demo/demo.ts @@ -290,25 +290,25 @@ export class ShellComponent {} template: `

    Specs Demo

    -
    +

    Input/Output Component

    -
    +

    External Template Component

    -
    +

    Component With External Template Component

    -
    +

    Reverse Pipe

    -
    +

    InputValueBinder Directive

    -
    +

    Button Component

    -
    +

    Needs Content

    diff --git a/adev/src/content/examples/testing/src/index.html b/adev/src/content/examples/testing/src/index.html index 8c6b39bc96a4..4106cb2d82e8 100644 --- a/adev/src/content/examples/testing/src/index.html +++ b/adev/src/content/examples/testing/src/index.html @@ -1,5 +1,5 @@ - + diff --git a/adev/src/content/examples/v21-game-world/src/app/app.ts b/adev/src/content/examples/v21-game-world/src/app/app.ts index 039a95d86db2..856ddce4e190 100644 --- a/adev/src/content/examples/v21-game-world/src/app/app.ts +++ b/adev/src/content/examples/v21-game-world/src/app/app.ts @@ -126,7 +126,10 @@ const DESTINATION_TOLERANCE = 0.005; @Component({ selector: 'app-root', template: ` -
    +
    @for (key of keysToShow(); track key) { @@ -235,9 +238,7 @@ const DESTINATION_TOLERANCE = 0.005; } @if (activeDestination() && (activeDestination()?.id !== 'd4' || allKeysCollected())) { - + }
    `, @@ -465,7 +466,10 @@ const DESTINATION_TOLERANCE = 0.005; font-size: 2cqw; font-weight: bold; cursor: pointer; - transition: background-color 0.2s, opacity 0.3s ease-in-out, visibility 0.3s ease-in-out; + transition: + background-color 0.2s, + opacity 0.3s ease-in-out, + visibility 0.3s ease-in-out; z-index: 20; opacity: 0; visibility: hidden; diff --git a/adev/src/content/examples/v21-game-world/src/index.html b/adev/src/content/examples/v21-game-world/src/index.html index 2b169cb432bf..bd7fff9e2b12 100644 --- a/adev/src/content/examples/v21-game-world/src/index.html +++ b/adev/src/content/examples/v21-game-world/src/index.html @@ -1,13 +1,13 @@ - - - v21 Game World - - - - - - - + + + v21 Game World + + + + + + + diff --git a/adev/src/content/guide/animations/complex-sequences.md b/adev/src/content/guide/animations/complex-sequences.md index 27a69f3879d9..ddd3fe3899dd 100644 --- a/adev/src/content/guide/animations/complex-sequences.md +++ b/adev/src/content/guide/animations/complex-sequences.md @@ -54,7 +54,7 @@ The following example demonstrates how to use the `query()` and `stagger()` func - Use `stagger()` to delay each animation by 30 milliseconds - Animate each element on screen for 0.5 seconds using a custom-defined easing curve, simultaneously fading it in and un-transforming it - + ## Parallel animation using group() function @@ -67,7 +67,7 @@ HELPFUL: The [`group()`](api/animations/group) function is used to group animati The following example uses [`group()`](api/animations/group)s on both `:enter` and `:leave` for two different timing configurations, thus applying two independent animations to the same element in parallel. - + ## Sequential vs. parallel animations @@ -91,11 +91,11 @@ The heroes list gradually re-enters the page as you delete each letter in the fi The HTML template contains a trigger called `filterAnimation`. - + The `filterAnimation` in the component's decorator contains three transitions. - + The code in this example performs the following tasks: diff --git a/adev/src/content/guide/animations/css.md b/adev/src/content/guide/animations/css.md index 0e68cf254042..a88a32ef70fd 100644 --- a/adev/src/content/guide/animations/css.md +++ b/adev/src/content/guide/animations/css.md @@ -20,7 +20,7 @@ Check some of these various guides and tutorials out, and then come back to this You can create reusable animations that can be shared across your application using `@keyframes`. Define keyframe animations in a shared CSS file, and you'll be able to re-use those keyframe animations wherever you want within your application. - + Adding the class `animated-class` to an element would trigger the animation on that element. @@ -30,7 +30,7 @@ Adding the class `animated-class` to an element would trigger the animation on t You may want to animate between two different states, for example when an element is opened or closed. You can accomplish this by using CSS classes either using a keyframe animation or transition styling. - + Triggering the `open` or `closed` state is done by toggling classes on the element in your component. You can find examples of how to do this in our [template guide](guide/templates/binding#css-class-and-style-property-bindings). @@ -42,11 +42,11 @@ Animating often requires adjusting timing, delays and easeing behaviors. This ca Specify `animation-duration`, `animation-delay`, and `animation-timing-function` for a keyframe animation in CSS, or alternatively use the `animation` shorthand property. - + Similarly, you can use `transition-duration`, `transition-delay`, and `transition-timing-function` and the `transition` shorthand for animations that are not using `@keyframes`. - + ### Triggering an Animation @@ -159,7 +159,9 @@ You can apply multiple animations to an element at once using the `animation` sh ```css .target-element { - animation: rotate 3s, fade-in 2s; + animation: + rotate 3s, + fade-in 2s; } ``` diff --git a/adev/src/content/guide/animations/enter-and-leave.md b/adev/src/content/guide/animations/enter-and-leave.md index 8ab48087deb7..2a994183a92f 100644 --- a/adev/src/content/guide/animations/enter-and-leave.md +++ b/adev/src/content/guide/animations/enter-and-leave.md @@ -86,7 +86,7 @@ TestBed provides built-in support for enabling or disabling animations in your t If you want to test that the animations are animating in a browser test, for example an end-to-end test, you can configure TestBed to enable animations by specifying `animationsEnabled: true` in your test configuration. ```typescript - TestBed.configureTestingModule({animationsEnabled: true}); +TestBed.configureTestingModule({animationsEnabled: true}); ``` This will configure animations in your test environment to behave normally. diff --git a/adev/src/content/guide/animations/migration.md b/adev/src/content/guide/animations/migration.md index 60890a17368b..7872a9b507f2 100644 --- a/adev/src/content/guide/animations/migration.md +++ b/adev/src/content/guide/animations/migration.md @@ -22,11 +22,11 @@ Just like with the animations package, you can create reusable animations that c #### With Animations Package - + #### With Native CSS - + Adding the class `animated-class` to an element would trigger the animation on that element. @@ -38,13 +38,13 @@ The animations package allowed you to define various states using the [`state()` #### With Animations Package - + This same behavior can be accomplished natively by using CSS classes either using a keyframe animation or transition styling. #### With Native CSS - + Triggering the `open` or `closed` state is done by toggling classes on the element in your component. You can find examples of how to do this in our [template guide](guide/templates/binding#css-class-and-style-property-bindings). @@ -56,11 +56,11 @@ The animations package `animate()` function allows for providing timing, like du Specify `animation-duration`, `animation-delay`, and `animation-timing-function` for a keyframe animation in CSS, or alternatively use the `animation` shorthand property. - + Similarly, you can use `transition-duration`, `transition-delay`, and `transition-timing-function` and the `transition` shorthand for animations that are not using `@keyframes`. - + ### Triggering an Animation @@ -245,7 +245,9 @@ The animations package has a `group()` function to play multiple animations at t ```css .target-element { - animation: rotate 3s, fade-in 2s; + animation: + rotate 3s, + fade-in 2s; } ``` diff --git a/adev/src/content/guide/animations/overview.md b/adev/src/content/guide/animations/overview.md index e1d042d6344f..04981eaa651e 100644 --- a/adev/src/content/guide/animations/overview.md +++ b/adev/src/content/guide/animations/overview.md @@ -32,13 +32,11 @@ To get started with adding Angular animations to your project, import the animat Import `provideAnimationsAsync` from `@angular/platform-browser/animations/async` and add it to the providers list in the `bootstrapApplication` function call. - +```ts {header: "Enabling Animations", linenums} bootstrapApplication(AppComponent, { - providers: [ - provideAnimationsAsync(), - ] + providers: [provideAnimationsAsync()], }); - +``` If you need to have an animation happen immediately when your application is loaded, @@ -54,7 +52,7 @@ For `NgModule` based applications import `BrowserAnimationsModule`, which introd If you plan to use specific animation functions in component files, import those functions from `@angular/animations`. - + See all [available animation functions](guide/legacy-animations#animations-api-summary) at the end of this guide. @@ -63,7 +61,7 @@ See all [available animation functions](guide/legacy-animations#animations-api-s In the component file, add a metadata property called `animations:` within the `@Component()` decorator. You put the trigger that defines an animation within the `animations` metadata property. - + @@ -101,11 +99,11 @@ Let's see how Angular's [`state()`](api/animations/state) function works with th In this code snippet, multiple style attributes are set at the same time for the state. In the `open` state, the button has a height of 200 pixels, an opacity of 1, and a yellow background color. - + In the following `closed` state, the button has a height of 100 pixels, an opacity of 0.8, and a background color of blue. - + ### Transitions and timing @@ -126,19 +124,15 @@ The `animate()` function \(second argument of the transition function\) accepts The `timings` parameter takes either a number or a string defined in three parts. - - -animate (duration) - - +```ts +animate(duration); +``` or - - -animate ('duration delay easing') - - +```ts +animate('duration delay easing'); +``` The first part, `duration`, is required. The duration can be expressed in milliseconds as a number without quotes, or in seconds with quotes and a time specifier. @@ -177,7 +171,7 @@ HELPFUL: See the Material Design website's topic on [Natural easing curves](http This example provides a state transition from `open` to `closed` with a 1-second transition between states. - + In the preceding code snippet, the `=>` operator indicates unidirectional transitions, and `<=>` is bidirectional. Within the transition, `animate()` specifies how long the transition takes. @@ -185,7 +179,7 @@ In this case, the state change from `open` to `closed` takes 1 second, expressed This example adds a state transition from the `closed` state to the `open` state with a 0.5-second transition animation arc. - + HELPFUL: Some additional notes on using styles within [`state`](api/animations/state) and `transition` functions. @@ -194,11 +188,9 @@ HELPFUL: Some additional notes on using styles within [`state`](api/animations/s - When animations are disabled, `transition()` styles can be skipped, but [`state()`](api/animations/state) styles can't - Include multiple state pairs within the same `transition()` argument: - - - transition( 'on => off, off => void' ) - - + ```ts + transition('on => off, off => void'); + ``` ### Triggering the animation @@ -220,22 +212,20 @@ However, it's possible for multiple triggers to be active at once. Animations are defined in the metadata of the component that controls the HTML element to be animated. Put the code that defines your animations under the `animations:` property within the `@Component()` decorator. - + When you've defined an animation trigger for a component, attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an `@` symbol. Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, where `triggerName` is the name of the trigger, and `expression` evaluates to a defined animation state. - - -
    ; - -
    +```angular-html +
    +``` The animation is executed or triggered when the expression value changes to a new state. The following code snippet binds the trigger to the value of the `isOpen` property. - + In this example, when the `isOpen` expression evaluates to a defined state of `open` or `closed`, it notifies the trigger `openClose` of a state change. Then it's up to the `openClose` code to handle the state change and kick off a state change animation. @@ -252,8 +242,8 @@ In the HTML template file, use the trigger name to attach the defined animations Here are the code files discussed in the transition example. - - + + diff --git a/adev/src/content/guide/animations/reusable-animations.md b/adev/src/content/guide/animations/reusable-animations.md index 17c2e9bdd8e1..350aaa4b4a42 100644 --- a/adev/src/content/guide/animations/reusable-animations.md +++ b/adev/src/content/guide/animations/reusable-animations.md @@ -9,7 +9,7 @@ This topic provides some examples of how to create reusable animations. To create a reusable animation, use the [`animation()`](api/animations/animation) function to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your application components using the [`useAnimation()`](api/animations/useAnimation) function. - + In the preceding code snippet, `transitionAnimation` is made reusable by declaring it as an export variable. @@ -18,12 +18,12 @@ HELPFUL: The `height`, `opacity`, `backgroundColor`, and `time` inputs are repla You can also export a part of an animation. For example, the following snippet exports the animation `trigger`. - + From this point, you can import reusable animation variables in your component class. For example, the following code snippet imports the `transitionAnimation` variable and uses it via the `useAnimation()` function. - + ## More on Angular animations diff --git a/adev/src/content/guide/animations/transition-and-triggers.md b/adev/src/content/guide/animations/transition-and-triggers.md index 13be61108daf..1856a2e2493c 100644 --- a/adev/src/content/guide/animations/transition-and-triggers.md +++ b/adev/src/content/guide/animations/transition-and-triggers.md @@ -23,11 +23,11 @@ Instead of defining each state-to-state transition pair, any transition to `clos This allows the addition of new states without having to include separate transitions for each one. - + Use a double arrow syntax to specify state-to-state transitions in both directions. - + ### Use wildcard state with multiple transition states @@ -37,7 +37,7 @@ If the button can change from `open` to either `closed` or something like `inPro wildcard state with 3 states - + The `* => *` transition applies when any change between two states takes place. @@ -52,7 +52,7 @@ To do this, list the more specific transitions _before_ `* => *`. Use the wildcard `*` with a style to tell the animation to use whatever the current style value is, and animate with that. Wildcard is a fallback value that's used if the state being animated isn't declared within the trigger. - + ### Void state @@ -76,7 +76,7 @@ Add a new behavior: - When you add a hero to the list of heroes, it appears to fly onto the page from the left - When you remove a hero from the list, it appears to fly out to the right - + In the preceding code, you applied the `void` state when the HTML element isn't attached to a view. @@ -85,12 +85,12 @@ In the preceding code, you applied the `void` state when the HTML element isn't `:enter` and `:leave` are aliases for the `void => *` and `* => void` transitions. These aliases are used by several animation functions. - +```ts {hideCopy} transition ( ':enter', [ … ] ); // alias for void => _ transition ( ':leave', [ … ] ); // alias for _ => void - +``` It's harder to target an element that is entering a view because it isn't in the DOM yet. Use the aliases `:enter` and `:leave` to target HTML elements that are inserted or removed from a view. @@ -105,11 +105,11 @@ As a rule of thumb consider that any element being added to the DOM by Angular p This example has a special trigger for the enter and leave animation called `myInsertRemoveTrigger`. The HTML template contains the following code. - + In the component file, the `:enter` transition sets an initial opacity of 0. It then animates it to change that opacity to 1 as the element is inserted into the view. - + Note that this example doesn't need to use [`state()`](api/animations/state). @@ -121,13 +121,13 @@ Use these to kick off a transition when a numeric value has increased or decreas HELPFUL: The following example uses `query()` and `stagger()` methods. For more information on these methods, see the [complex sequences](guide/legacy-animations/complex-sequences) page. - + ## Boolean values in transitions If a trigger contains a Boolean value as a binding value, then this value can be matched using a `transition()` expression that compares `true` and `false`, or `1` and `0`. - + In the code snippet above, the HTML template binds a `
    ` element to a trigger named `openClose` with a status expression of `isOpen`, and with possible values of `true` and `false`. This pattern is an alternative to the practice of creating two named states like `open` and `close`. @@ -136,7 +136,7 @@ Inside the `@Component` metadata under the `animations:` property, when the stat In this case, the animation uses whatever height the element already had before the animation started. When the element is `closed`, the element gets animated to a height of 0, which makes it invisible. - + ## Multiple animation triggers @@ -156,8 +156,8 @@ When true, the `@.disabled` binding prevents all animations from rendering. The following code sample shows how to use this feature. - - + + When the `@.disabled` binding is true, the `@childAnimation` trigger doesn't kick off. @@ -177,7 +177,7 @@ Those elements can still animate. To turn off all animations for an Angular application, place the `@.disabled` host binding on the topmost Angular component. - + HELPFUL: Disabling animations application-wide is useful during end-to-end \(E2E\) testing. @@ -186,12 +186,12 @@ HELPFUL: Disabling animations application-wide is useful during end-to-end \(E2E The animation `trigger()` function emits _callbacks_ when it starts and when it finishes. The following example features a component that contains an `openClose` trigger. - + In the HTML template, the animation event is passed back via `$event`, as `@triggerName.start` and `@triggerName.done`, where `triggerName` is the name of the trigger being used. In this example, the trigger `openClose` appears as follows. - + A potential use for animation callbacks could be to cover for a slow API call, such as a database lookup. For example, an **InProgress** button can be set up to have its own looping animation while the backend system operation finishes. @@ -204,7 +204,7 @@ An animation can influence an end user to _perceive_ the operation as faster, ev Callbacks can serve as a debugging tool, for example in conjunction with `console.warn()` to view the application's progress in a browser's Developer JavaScript Console. The following code snippet creates console log output for the original example, a button with the two states of `open` and `closed`. - + ## Keyframes @@ -217,7 +217,7 @@ For example, the button, instead of fading, could change color several times ove The code for this color change might look like this. - + ### Offset @@ -233,7 +233,7 @@ Specifying an offset of 0.8 for the middle transition in the preceding example m The code with offsets specified would be as follows. - + You can combine keyframes with `duration`, `delay`, and `easing` within a single animation. @@ -250,7 +250,7 @@ Here's an example of using keyframes to create a pulse effect: The code snippet for this animation might look like this. - + ### Animatable properties and units @@ -285,7 +285,7 @@ In these cases, you can use a special wildcard `*` property value under `style() The following example has a trigger called `shrinkOut`, used when an HTML element leaves the page. The animation takes whatever height the element has before it leaves, and animates from that height to zero. - + ### Keyframes summary diff --git a/adev/src/content/guide/aria/BUILD.bazel b/adev/src/content/guide/aria/BUILD.bazel index 2aa462faedde..0404e4f11507 100644 --- a/adev/src/content/guide/aria/BUILD.bazel +++ b/adev/src/content/guide/aria/BUILD.bazel @@ -6,6 +6,7 @@ generate_guides( srcs = glob([ "*.md", ]), + api_manifest = "//adev/src/assets:docs_api_manifest", data = [ "//adev/src/content/examples", ], diff --git a/adev/src/content/guide/aria/combobox.md b/adev/src/content/guide/aria/combobox.md index 71390217ca46..38681f5d814d 100644 --- a/adev/src/content/guide/aria/combobox.md +++ b/adev/src/content/guide/aria/combobox.md @@ -51,7 +51,7 @@ Use documented patterns instead when: - Single-selection dropdowns are needed - See the [Select pattern](guide/aria/select) for complete dropdown implementation - Multiple-selection dropdowns are needed - See the [Multiselect pattern](guide/aria/multiselect) for multi-select with compact display -Note: The [Autocomplete](guide/aria/autocomplete), [Select](guide/aria/select), and [Multiselect](guide/aria/multiselect) guides show documented patterns that combine this directive with [Listbox](guide/aria/listbox) for specific use cases. +NOTE: The [Autocomplete](guide/aria/autocomplete), [Select](guide/aria/select), and [Multiselect](guide/aria/multiselect) guides show documented patterns that combine this directive with [Listbox](guide/aria/listbox) for specific use cases. ## Features diff --git a/adev/src/content/guide/aria/listbox.md b/adev/src/content/guide/aria/listbox.md index a1863dc15141..d7a753a2d9a0 100644 --- a/adev/src/content/guide/aria/listbox.md +++ b/adev/src/content/guide/aria/listbox.md @@ -108,14 +108,19 @@ With `orientation="horizontal"`, left and right arrow keys navigate between opti ### Selection modes -Listbox supports two selection modes that control when items become selected. Choose the mode that matches your interface's interaction pattern. +Listbox supports two selection modes that control when items become selected. - + + +| Mode | Description | +| ------------ | ------------------------------------------------------------------------------------------------------ | +| `'follow'` | Automatically selects the focused item, providing faster interaction when selection changes frequently | +| `'explicit'` | Requires Space or Enter to confirm selection, preventing accidental changes while navigating | -The `'follow'` mode automatically selects the focused item, providing faster interaction when selection changes frequently. The `'explicit'` mode requires Space or Enter to confirm selection, preventing accidental changes while navigating. Dropdown patterns typically use `'follow'` mode for single selection. +TIP: Dropdown patterns typically use `'follow'` mode for single selection. ## APIs diff --git a/adev/src/content/guide/aria/toolbar.md b/adev/src/content/guide/aria/toolbar.md index 3f204d5cc089..926e8c4410ee 100644 --- a/adev/src/content/guide/aria/toolbar.md +++ b/adev/src/content/guide/aria/toolbar.md @@ -135,22 +135,14 @@ The `multi` input controls whether multiple widgets within a group can be select ```html {highlight: [15]} -
    +
    -
    +
    diff --git a/adev/src/content/guide/components/anatomy-of-components.md b/adev/src/content/guide/components/anatomy-of-components.md index 5a301b26aaf6..c9732799a096 100644 --- a/adev/src/content/guide/components/anatomy-of-components.md +++ b/adev/src/content/guide/components/anatomy-of-components.md @@ -11,13 +11,13 @@ Every component must have: You provide Angular-specific information for a component by adding a `@Component` [decorator](https://www.typescriptlang.org/docs/handbook/decorators.html) on top of the TypeScript class: - +```angular-ts {highlight: [1, 2, 3, 4]} @Component({ selector: 'profile-photo', template: `Your profile photo`, }) export class ProfilePhoto { } - +``` For full details on writing Angular templates, including data binding, event handling, and control flow, see the [Templates guide](guide/templates). @@ -25,27 +25,27 @@ The object passed to the `@Component` decorator is called the component's **meta Components can optionally include a list of CSS styles that apply to that component's DOM: - +```angular-ts {highlight: [4]} @Component({ selector: 'profile-photo', template: `Your profile photo`, styles: `img { border-radius: 50%; }`, }) export class ProfilePhoto { } - +``` By default, a component's styles only affect elements defined in that component's template. See [Styling Components](guide/components/styling) for details on Angular's approach to styling. You can alternatively choose to write your template and styles in separate files: - +```ts {highlight: [3,4]} @Component({ selector: 'profile-photo', templateUrl: 'profile-photo.html', styleUrl: 'profile-photo.css', }) -export class ProfilePhoto { } - +export class ProfilePhoto {} +``` This can help separate the concerns of _presentation_ from _behavior_ in your project. You can choose one approach for your entire project, or you decide which to use for each component. @@ -67,7 +67,7 @@ import {ProfilePhoto} from './profile-photo'; imports: [ProfilePhoto], /* ... */ }) -export class UserProfile { } +export class UserProfile {} ``` By default, Angular components are _standalone_, meaning that you can directly add them to the `imports` array of other components. Components created with an earlier version of Angular may instead specify `standalone: false` in their `@Component` decorator. For these components, you instead import the `NgModule` in which the component is defined. See the full [`NgModule` guide](guide/ngmodules) for details. @@ -78,19 +78,19 @@ Important: In Angular versions before 19.0.0, the `standalone` option defaults t Every component defines a [CSS selector](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors): - +```angular-ts {highlight: [2]} @Component({ selector: 'profile-photo', ... }) export class ProfilePhoto { } - +``` See [Component Selectors](guide/components/selectors) for details about which types of selectors Angular supports and guidance on choosing a selector. You show a component by creating a matching HTML element in the template of _other_ components: - +```angular-ts {highlight: [8]} @Component({ selector: 'profile-photo', }) @@ -101,7 +101,7 @@ imports: [ProfilePhoto], template: `` }) export class UserProfile { } - +``` Angular creates an instance of the component for every matching HTML element it encounters. The DOM element that matches a component's selector is referred to as that component's **host element**. The contents of a component's template are rendered inside its host element. diff --git a/adev/src/content/guide/components/content-projection.md b/adev/src/content/guide/components/content-projection.md index efa351cabf9a..3fe55711f927 100644 --- a/adev/src/content/guide/components/content-projection.md +++ b/adev/src/content/guide/components/content-projection.md @@ -95,7 +95,7 @@ export class CardBody {} ```angular-ts -Component({ +@Component({ selector: 'custom-card', template: `
    @@ -218,7 +218,7 @@ placeholder, Angular compares against the `ngProjectAs` value instead of the ele
    - +
    ``` diff --git a/adev/src/content/guide/components/host-elements.md b/adev/src/content/guide/components/host-elements.md index 38f5080b34ce..fc200dac2e8b 100644 --- a/adev/src/content/guide/components/host-elements.md +++ b/adev/src/content/guide/components/host-elements.md @@ -150,7 +150,7 @@ export class MyComponent { In this example, the `--my-background` CSS custom property is bound to the `color` signal. The value of the custom property will automatically update whenever the `color` signal changes. This will affect the current component and all its children that rely on this custom property. -### Setting custom properties on children compoents +### Setting custom properties on children components Alternatively, it is also possible to set css custom properties on the host element of children components with a [style binding](guide/templates/binding#css-style-properties). diff --git a/adev/src/content/guide/components/inputs.md b/adev/src/content/guide/components/inputs.md index 73651d757389..0b2d8236f967 100644 --- a/adev/src/content/guide/components/inputs.md +++ b/adev/src/content/guide/components/inputs.md @@ -10,7 +10,9 @@ When you use a component, you commonly want to pass some data to it. A component ```ts {highlight:[5]} import {Component, input} from '@angular/core'; -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { // Declare an input named 'value' with a default value of zero. value = input(0); @@ -26,7 +28,9 @@ This lets you bind to the property in a template: If an input has a default value, TypeScript infers the type from the default value: ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { // TypeScript infers that this input is a number, returning InputSignal. value = input(0); @@ -38,7 +42,9 @@ You can explicitly declare a type for the input by specifying a generic paramete If an input without a default value is not set, its value is `undefined`: ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { // Produces an InputSignal because `value` may not be set. value = input(); @@ -60,7 +66,9 @@ The `input` function returns an `InputSignal`. You can read the value by calling ```ts {highlight:[5]} import {Component, input, computed} from '@angular/core'; -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { // Declare an input named 'value' with a default value of zero. value = input(0); @@ -77,7 +85,9 @@ Signals created by the `input` function are read-only. You can declare that an input is `required` by calling `input.required` instead of `input`: ```ts {highlight:[3]} -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { // Declare a required input named value. Returns an `InputSignal`. value = input.required(); @@ -127,7 +137,9 @@ The most common use-case for input transforms is to accept a wider range of valu When you specify an input transform, the type of the transform function's parameter determines the types of values that can be set to the input in a template. ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { widthPx = input('', {transform: appendPx}); } @@ -146,7 +158,9 @@ Angular includes two built-in transform functions for the two most common scenar ```ts import {Component, input, booleanAttribute, numberAttribute} from '@angular/core'; -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { disabled = input(false, {transform: booleanAttribute}); value = input(0, {transform: numberAttribute}); @@ -163,7 +177,9 @@ _presence_ of the attribute indicates a "true" value. However, Angular's `boolea You can specify the `alias` option to change the name of an input in templates. ```ts {highlight:[3]} -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { value = input(0, {alias: 'sliderValue'}); } @@ -186,14 +202,16 @@ When creating a component, you can define a model input similarly to how you cre Both types of input allow someone to bind a value into the property. However, **model inputs allow the component author to write values into the property**. If the property is bound with a two-way binding, the new value propagates to that binding. ```ts -@Component({ /* ... */}) +@Component({ + /* ... */ +}) export class CustomSlider { // Define a model input named "value". value = model(0); increment() { // Update the model input with a new value, propagating the value to any bindings. - this.value.update(oldValue => oldValue + 10); + this.value.update((oldValue) => oldValue + 10); } } @@ -212,7 +230,7 @@ export class MediaControls { In the above example, the `CustomSlider` can write values into its `value` model input, which then propagates those values back to the `volume` signal in `MediaControls`. This binding keeps the values of `value` and `volume` in sync. Notice that the binding passes the `volume` signal instance, not the _value_ of the signal. -In other respects, model inputs work similarly to standard inputs. You can read the value by calling the signal function, including in reactive contexts like `computed` and `effect`. +In other respects, model inputs work similarly to standard inputs. You can read the value by calling the signal function, including in [reactive contexts](guide/signals#reactive-contexts) like `computed` and `effect`. See [Two-way binding](guide/templates/two-way-binding) for more details on two-way binding in templates. @@ -239,7 +257,9 @@ In the example above, the `CustomSlider` can write values into its `value` model When you declare a model input in a component or directive, Angular automatically creates a corresponding [output](guide/components/outputs) for that model. The output's name is the model input's name suffixed with "Change". ```ts -@Directive({ /* ... */ }) +@Directive({ + /* ... */ +}) export class CustomCheckbox { // This automatically creates an output named "checkedChange". // Can be subscribed to using `(checkedChange)="handler()"` in the template. @@ -351,7 +371,9 @@ export class CustomSlider { return this.internalValue; } - set value(newValue: number) { this.internalValue = newValue; } + set value(newValue: number) { + this.internalValue = newValue; + } private internalValue = 0; } diff --git a/adev/src/content/guide/components/lifecycle.md b/adev/src/content/guide/components/lifecycle.md index 6f075427532f..177fc5a967c8 100644 --- a/adev/src/content/guide/components/lifecycle.md +++ b/adev/src/content/guide/components/lifecycle.md @@ -262,7 +262,7 @@ export class UserProfile { private elementHeight = 0; constructor() { - private elementRef = inject(ElementRef); + const elementRef = inject(ElementRef); const nativeElement = elementRef.nativeElement; afterNextRender({ diff --git a/adev/src/content/guide/components/outputs.md b/adev/src/content/guide/components/outputs.md index 57a302cc3e70..b21550ef9e81 100644 --- a/adev/src/content/guide/components/outputs.md +++ b/adev/src/content/guide/components/outputs.md @@ -5,7 +5,9 @@ TIP: This guide assumes you've already read the [Essentials Guide](essentials). Angular components can define custom events by assigning a property to the `output` function: ```ts {highlight:[3]} -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class ExpandablePanel { panelClosed = output(); } @@ -18,7 +20,7 @@ export class ExpandablePanel { The `output` function returns an `OutputEmitterRef`. You can emit an event by calling the `emit` method on the `OutputEmitterRef`: ```ts - this.panelClosed.emit(); +this.panelClosed.emit(); ``` Angular refers to properties initialized with the `output` function as **outputs**. You can use outputs to raise custom events, similar to native browser events like `click`. @@ -43,7 +45,7 @@ this.valueChanged.emit(7); this.thumbDropped.emit({ pointerX: 123, pointerY: 456, -}) +}); ``` When defining an event listener in a template, you can access the event data from the `$event` variable: @@ -71,7 +73,9 @@ export class App { The `output` function accepts a parameter that lets you specify a different name for the event in a template: ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { changed = output({alias: 'valueChanged'}); } @@ -93,7 +97,7 @@ from the component instance. The `OutputRef` type includes a `subscribe` method: ```ts const someComponentRef: ComponentRef = viewContainerRef.createComponent(/*...*/); -someComponentRef.instance.someEventProperty.subscribe(eventData => { +someComponentRef.instance.someEventProperty.subscribe((eventData) => { console.log(eventData); }); ``` @@ -101,7 +105,7 @@ someComponentRef.instance.someEventProperty.subscribe(eventData => { Angular automatically cleans up event subscriptions when it destroys components with subscribers. Alternatively, you can manually unsubscribe from an event. The `subscribe` function returns an `OutputRefSubscription` with an `unsubscribe` method: ```ts -const eventSubscription = someComponent.someEventProperty.subscribe(eventData => { +const eventSubscription = someComponent.someEventProperty.subscribe((eventData) => { console.log(eventData); }); @@ -130,7 +134,9 @@ original decorator-based `@Output` API remains fully supported. You can alternatively define custom events by assigning a property to a new `EventEmitter` and adding the `@Output` decorator: ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class ExpandablePanel { @Output() panelClosed = new EventEmitter(); } @@ -143,7 +149,9 @@ You can emit an event by calling the `emit` method on the `EventEmitter`. The `@Output` decorator accepts a parameter that lets you specify a different name for the event in a template: ```ts -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomSlider { @Output('valueChanged') changed = new EventEmitter(); } diff --git a/adev/src/content/guide/components/programmatic-rendering.md b/adev/src/content/guide/components/programmatic-rendering.md index 51887ae3591b..f1d2a2f0518d 100644 --- a/adev/src/content/guide/components/programmatic-rendering.md +++ b/adev/src/content/guide/components/programmatic-rendering.md @@ -174,12 +174,20 @@ export class AppWarningComponent { ``` ```ts -import { Component, ViewContainerRef, signal, inputBinding, outputBinding, twoWayBinding, inject } from '@angular/core'; -import { FocusTrap } from "@angular/cdk/a11y"; -import { ThemeDirective } from '../theme.directive'; +import { + Component, + ViewContainerRef, + signal, + inputBinding, + outputBinding, + twoWayBinding, + inject, +} from '@angular/core'; +import {FocusTrap} from '@angular/cdk/a11y'; +import {ThemeDirective} from '../theme.directive'; @Component({ - template: `` + template: ``, }) export class HostComponent { private vcr = inject(ViewContainerRef); @@ -193,12 +201,12 @@ export class HostComponent { twoWayBinding('isExpanded', this.isExpanded), outputBinding('close', (confirmed) => { console.log('Closed with result:', confirmed); - }) + }), ], directives: [ FocusTrap, - { type: ThemeDirective, bindings: [inputBinding('theme', () => 'warning')] } - ] + {type: ThemeDirective, bindings: [inputBinding('theme', () => 'warning')]}, + ], }); } } @@ -220,9 +228,9 @@ import { inputBinding, outputBinding, } from '@angular/core'; -import { PopupComponent } from './popup.component'; +import {PopupComponent} from './popup.component'; -@Injectable({ providedIn: 'root' }) +@Injectable({providedIn: 'root'}) export class PopupService { private readonly injector = inject(EnvironmentInjector); private readonly appRef = inject(ApplicationRef); diff --git a/adev/src/content/guide/components/queries.md b/adev/src/content/guide/components/queries.md index 075d2c738afc..1404ffee902b 100644 --- a/adev/src/content/guide/components/queries.md +++ b/adev/src/content/guide/components/queries.md @@ -7,7 +7,7 @@ A component can define **queries** that find child elements and read values from Developers most commonly use queries to retrieve references to child components, directives, DOM elements, and more. All query functions return signals that reflect the most up-to-date results. You can read the -result by calling the signal function, including in reactive contexts like `computed` and `effect`. +result by calling the signal function, including in [reactive contexts](guide/signals#reactive-contexts) like `computed` and `effect`. There are two categories of query: **view queries** and **content queries.** @@ -15,7 +15,7 @@ There are two categories of query: **view queries** and **content queries.** View queries retrieve results from the elements in the component's _view_ — the elements defined in the component's own template. You can query for a single result with the `viewChild` function. -```typescript {highlight: [14, 15]} +```angular-ts {highlight: [14, 15]} @Component({ selector: 'custom-card-header', /*...*/ @@ -40,7 +40,7 @@ If the query does not find a result, its value is `undefined`. This may occur if You can also query for multiple results with the `viewChildren` function. -```typescript {highlight: [17]} +```angular-ts {highlight: [17]} @Component({ selector: 'custom-card-action', /*...*/ @@ -51,13 +51,14 @@ export class CustomCardAction { @Component({ selector: 'custom-card', - template: `Save + template: ` + Save Cancel `, }) export class CustomCard { actions = viewChildren(CustomCardAction); - actionsTexts = computed(() => this.actions().map(action => action.text); + actionsTexts = computed(() => this.actions().map(action => action.text)); } ``` @@ -69,7 +70,7 @@ export class CustomCard { Content queries retrieve results from the elements in the component's _content_— the elements nested inside the component in the template where it's used. You can query for a single result with the `contentChild` function. -```typescript {highlight: [14, 15]} +```angular-ts {highlight: [14, 15]} @Component({ selector: 'custom-toggle', /*...*/ @@ -106,7 +107,7 @@ By default, content queries find only _direct_ children of the component and do You can also query for multiple results with the `contentChildren` function. -```typescript {highlight: [14, 16, 17, 18, 19, 20]} +```angular-ts {highlight: [14, 16, 17, 18, 19, 20]} @Component({ selector: 'custom-menu-item', /*...*/ @@ -147,8 +148,10 @@ If a child query (`viewChild` or `contentChild`) does not find a result, its val In some cases, especially with `viewChild`, you know with certainty that a specific child is always available. In other cases, you may want to strictly enforce that a specific child is present. For these cases, you can use a _required query_. -```angular-ts -@Component({/* ... */}) +```ts +@Component({ + /* ... */ +}) export class CustomCard { header = viewChild.required(CustomCardHeader); body = contentChild.required(CustomCardBody); @@ -215,8 +218,9 @@ All query functions accept an options object as a second parameter. These option By default, the query locator indicates both the element you're searching for and the value retrieved. You can alternatively specify the `read` option to retrieve a different value from the element matched by the locator. ```ts - -@Component({/*...*/}) +@Component({ + /*...*/ +}) export class CustomExpando { toggle = contentChild(ExpandoContent, {read: TemplateRef}); } @@ -232,7 +236,7 @@ Developers most commonly use `read` to retrieve `ElementRef` and `TemplateRef`. By default, `contentChildren` queries find only _direct_ children of the component and do not traverse into descendants. `contentChild` queries do traverse into descendants by default. -```typescript {highlight: [13, 14, 15, 16]} +```angular-ts {highlight: [13, 14, 15, 16]} @Component({ selector: 'custom-expando', /*...*/ @@ -244,7 +248,8 @@ export class CustomExpando { @Component({ selector: 'user-profile', - template: ` + template: ` + Show @@ -269,7 +274,7 @@ You can alternatively declare queries by adding the corresponding decorator to a You can query for a single result with the `@ViewChild` decorator. -```typescript {highlight: [14, 16, 17, 18]} +```angular-ts {highlight: [14, 16, 17, 18]} @Component({ selector: 'custom-card-header', /*...*/ @@ -299,7 +304,7 @@ Angular keeps the result of `@ViewChild` up to date as your application state ch You can also query for multiple results with the `@ViewChildren` decorator. -```typescript {highlight: [17, 19, 20, 21, 22, 23]} +```angular-ts {highlight: [17, 19, 20, 21, 22, 23]} @Component({ selector: 'custom-card-action', /*...*/ @@ -332,7 +337,7 @@ export class CustomCard { You can query for a single result with the `@ContentChild` decorator. -```typescript {highlight: [14, 16, 17, 18, 25]} +```angular-ts {highlight: [14, 16, 17, 18, 25]} @Component({ selector: 'custom-toggle', /*...*/ @@ -373,7 +378,7 @@ Angular keeps the result of `@ContentChild` up to date as your application state You can also query for multiple results with the `@ContentChildren` decorator. -```typescript {highlight: [15, 17, 18, 19, 20, 21]} +```angular-ts {highlight: [15, 17, 18, 19, 20, 21]} @Component({ selector: 'custom-menu-item', /*...*/ diff --git a/adev/src/content/guide/components/selectors.md b/adev/src/content/guide/components/selectors.md index b473b4d14a2e..8539c7f18c38 100644 --- a/adev/src/content/guide/components/selectors.md +++ b/adev/src/content/guide/components/selectors.md @@ -6,17 +6,17 @@ Every component defines a [CSS selector](https://developer.mozilla.org/docs/Web/CSS/CSS_selectors) that determines how the component is used: - +```angular-ts {highlight: [2]} @Component({ selector: 'profile-photo', ... }) export class ProfilePhoto { } - +``` You use a component by creating a matching HTML element in the templates of _other_ components: - +```angular-ts {highlight: [3]} @Component({ template: ` @@ -24,7 +24,7 @@ You use a component by creating a matching HTML element in the templates of _oth ..., }) export class UserProfile { } - +``` **Angular matches selectors statically at compile-time**. Changing the DOM at run-time, either via Angular bindings or with DOM APIs, does not affect the components rendered. @@ -63,13 +63,13 @@ You can append this pseudo-class to any other selector to narrow which elements selector matches. For example, you could define a `[dropzone]` attribute selector and prevent matching `textarea` elements: - +```angular-ts {highlight: [2]} @Component({ selector: '[dropzone]:not(textarea)', ... }) export class DropZone { } - +``` Angular does not support any other pseudo-classes or pseudo-elements in component selectors. @@ -78,23 +78,23 @@ Angular does not support any other pseudo-classes or pseudo-elements in componen You can combine multiple selectors by concatenating them. For example, you can match `` + template: ``, }) export class MyComponent { @Output() someChange = new EventEmitter(); @@ -42,7 +42,7 @@ export class MyComponent { import {Component, output} from '@angular/core'; @Component({ - template: `` + template: ``, }) export class MyComponent { readonly someChange = output(); diff --git a/adev/src/content/reference/migrations/overview.md b/adev/src/content/reference/migrations/overview.md index 026e27d434e7..4a6b17f66890 100644 --- a/adev/src/content/reference/migrations/overview.md +++ b/adev/src/content/reference/migrations/overview.md @@ -10,7 +10,7 @@ Learn about how you can migrate your existing angular project to the latest feat Built-in Control Flow Syntax allows you to use more ergonomic syntax which is close to JavaScript and has better type checking. It replaces the need to import `CommonModule` to use functionality like `*ngFor`, `*ngIf` and `*ngSwitch`. - Angular's `inject` function offers more accurate types and better compatibility with standard decorators, compared to constructor-based injection. + Angular's [`inject`](/api/core/inject) function offers more accurate types and better compatibility with standard decorators, compared to constructor-based injection. Convert eagerly loaded component routes to lazy loaded ones. This allows the build process to split production bundles into smaller chunks, to load less JavaScript at initial page load. diff --git a/adev/src/content/reference/migrations/route-lazy-loading.md b/adev/src/content/reference/migrations/route-lazy-loading.md index 66db02021077..1ae553df5730 100644 --- a/adev/src/content/reference/migrations/route-lazy-loading.md +++ b/adev/src/content/reference/migrations/route-lazy-loading.md @@ -60,7 +60,7 @@ export class AppModule {} { path: 'home', // ↓ HomeComponent is now lazy loaded - loadComponent: () => import('./home/home.component').then(m => m.HomeComponent), + loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent), }, ]), ], diff --git a/adev/src/content/reference/migrations/router-testing-module-migration.md b/adev/src/content/reference/migrations/router-testing-module-migration.md index 4cf969e75d40..2186c6086931 100644 --- a/adev/src/content/reference/migrations/router-testing-module-migration.md +++ b/adev/src/content/reference/migrations/router-testing-module-migration.md @@ -23,34 +23,30 @@ ng generate @angular/core:router-testing-module-migration Before: ```ts -import { RouterTestingModule } from '@angular/router/testing'; -import { SpyLocation } from '@angular/common/testing'; +import {RouterTestingModule} from '@angular/router/testing'; +import {SpyLocation} from '@angular/common/testing'; describe('test', () => { - beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule.withRoutes(routes, { initialNavigation: 'enabledBlocking' })] + imports: [RouterTestingModule.withRoutes(routes, {initialNavigation: 'enabledBlocking'})], }); }); - }); ``` After: ```ts -import { RouterModule } from '@angular/router'; -import { SpyLocation } from '@angular/common/testing'; +import {RouterModule} from '@angular/router'; +import {SpyLocation} from '@angular/common/testing'; describe('test', () => { - beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterModule.forRoot(routes, { initialNavigation: 'enabledBlocking' })] + imports: [RouterModule.forRoot(routes, {initialNavigation: 'enabledBlocking'})], }); }); - }); ``` @@ -59,45 +55,45 @@ describe('test', () => { Before: ```ts -import { RouterTestingModule } from '@angular/router/testing'; -import { SpyLocation } from '@angular/common/testing'; +import {RouterTestingModule} from '@angular/router/testing'; +import {SpyLocation} from '@angular/common/testing'; describe('test', () => { - let spy : SpyLocation; + let spy: SpyLocation; beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule] + imports: [RouterTestingModule], }); spy = TestBed.inject(SpyLocation); }); it('Awesome test', () => { - expect(spy.urlChanges).toBeDefined() - }) + expect(spy.urlChanges).toBeDefined(); + }); }); ``` After: ```ts -import { RouterModule } from '@angular/router'; -import { provideLocationMocks } from '@angular/common/testing'; -import { SpyLocation } from '@angular/common/testing'; +import {RouterModule} from '@angular/router'; +import {provideLocationMocks} from '@angular/common/testing'; +import {SpyLocation} from '@angular/common/testing'; describe('test', () => { - let spy : SpyLocation; + let spy: SpyLocation; beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterModule], - providers: [provideLocationMocks()] + providers: [provideLocationMocks()], }); spy = TestBed.inject(SpyLocation); }); it('Awesome test', () => { - expect(spy.urlChanges).toBeDefined() - }) + expect(spy.urlChanges).toBeDefined(); + }); }); ``` diff --git a/adev/src/content/reference/migrations/standalone.md b/adev/src/content/reference/migrations/standalone.md index 8b17b697b823..472f74df2c26 100644 --- a/adev/src/content/reference/migrations/standalone.md +++ b/adev/src/content/reference/migrations/standalone.md @@ -72,7 +72,7 @@ HELPFUL: The schematic ignores NgModules which bootstrap a component during this @NgModule({ imports: [CommonModule], declarations: [GreeterComponent], - exports: [GreeterComponent] + exports: [GreeterComponent], }) export class SharedModule {} ``` @@ -95,7 +95,7 @@ export class GreeterComponent { // shared.module.ts @NgModule({ imports: [CommonModule, GreeterComponent], - exports: [GreeterComponent] + exports: [GreeterComponent], }) export class SharedModule {} ``` @@ -134,7 +134,7 @@ The migration considers a module safe to remove if that module: // importer.module.ts @NgModule({ imports: [FooComponent, BarPipe], - exports: [FooComponent, BarPipe] + exports: [FooComponent, BarPipe], }) export class ImporterModule {} ``` @@ -154,12 +154,12 @@ This step converts any usages of `bootstrapModule` to the new, standalone-based ```typescript // ./app/app.module.ts -import { NgModule } from '@angular/core'; -import { AppComponent } from './app.component'; +import {NgModule} from '@angular/core'; +import {AppComponent} from './app.component'; @NgModule({ declarations: [AppComponent], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule {} ``` @@ -176,10 +176,12 @@ export class AppComponent {} ```typescript // ./main.ts -import { platformBrowser } from '@angular/platform-browser'; -import { AppModule } from './app/app.module'; +import {platformBrowser} from '@angular/platform-browser'; +import {AppModule} from './app/app.module'; -platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e)); +platformBrowser() + .bootstrapModule(AppModule) + .catch((e) => console.error(e)); ``` **After:** @@ -193,17 +195,17 @@ platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e)); // ./app/app.component.ts @Component({ selector: 'app', - template: 'hello' + template: 'hello', }) export class AppComponent {} ``` ```typescript // ./main.ts -import { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app/app.component'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {AppComponent} from './app/app.component'; -bootstrapApplication(AppComponent).catch(e => console.error(e)); +bootstrapApplication(AppComponent).catch((e) => console.error(e)); ``` ## Common problems diff --git a/adev/src/content/reference/roadmap.md b/adev/src/content/reference/roadmap.md index 35a61fc22736..924448533f7e 100644 --- a/adev/src/content/reference/roadmap.md +++ b/adev/src/content/reference/roadmap.md @@ -149,7 +149,7 @@ To make it easier for developers to use modern Angular APIs, we enabled integrat As part of this initiative, the language service automatically imports components and pipes in standalone and NgModule-based apps. Additionally, we've added a template diagnostic to highlight unused imports in standalone components, which should help make application bundles smaller. -We've released the support for local template variables in Angular, see [`@let` docs](https://angular.dev/api/core/@let) for additional information. +We've released the support for local template variables in Angular, see [`@let` docs](/api/core/@let) for additional information. To provide better customization of our Angular Material components and enable Material 3 capabilities, we'll be collaborating with Google's Material Design team on defining token-based theming APIs. @@ -172,7 +172,7 @@ Angular.dev is the new site, domain and home for Angular development. The new si In v17 we shipped a developer preview version of a new control flow. It brings significant performance improvements and better ergonomics for template authoring. We also provided a migration of existing `*ngIf`, `*ngFor`, and `*ngSwitch` which you can run to move your project to the new implementation. As of v18 the built-in control flow is now stable. -Over the past two quarters, we developed a new [video](https://www.youtube.com/watch?v=xAT0lHYhHMY&list=PL1w1q3fL4pmj9k1FrJ3Pe91EPub2_h4jF) and [textual](https://angular.dev/tutorials/learn-angular) tutorial based on standalone components. +Over the past two quarters, we developed a new [video](https://www.youtube.com/watch?v=xAT0lHYhHMY&list=PL1w1q3fL4pmj9k1FrJ3Pe91EPub2_h4jF) and [textual](/tutorials/learn-angular) tutorial based on standalone components. In Angular v16, we released a developer preview of an esbuild-based builder with support for `ng build` and `ng serve`. The `ng serve` development server uses Vite and a multi-file compilation by esbuild and the Angular compiler. In v17 we graduated the build tooling from developer preview and enabled it by default for new projects. diff --git a/adev/src/content/tools/cli/aot-compiler.md b/adev/src/content/tools/cli/aot-compiler.md index ef6aa6f00b0e..afa68a38d7e5 100644 --- a/adev/src/content/tools/cli/aot-compiler.md +++ b/adev/src/content/tools/cli/aot-compiler.md @@ -308,11 +308,9 @@ The compiler, however, only supports macros in the form of functions or static m For example, consider the following function: ```ts - export function wrapInArray(value: T): T[] { return [value]; } - ``` You can call the `wrapInArray` in a metadata definition because it returns the value of an expression that conforms to the compiler's restrictive JavaScript subset. @@ -320,23 +318,19 @@ You can call the `wrapInArray` in a metadata definition because it returns the v You might use `wrapInArray()` like this: ```ts - @NgModule({ - declarations: wrapInArray(TypicalComponent) + declarations: wrapInArray(TypicalComponent), }) export class TypicalModule {} - ``` The compiler treats this usage as if you had written: ```ts - @NgModule({ - declarations: [TypicalComponent] + declarations: [TypicalComponent], }) export class TypicalModule {} - ``` The Angular [`RouterModule`](api/router/RouterModule) exports two macro static methods, `forRoot` and `forChild`, to help declare root and child routes. @@ -352,34 +346,26 @@ the compiler doesn't need to know the expression's value — it just needs to be You might write something like: ```ts - -class TypicalServer { - -} +class TypicalServer {} @NgModule({ - providers: [{provide: SERVER, useFactory: () => TypicalServer}] + providers: [{provide: SERVER, useFactory: () => TypicalServer}], }) export class TypicalModule {} - ``` Without rewriting, this would be invalid because lambdas are not supported and `TypicalServer` is not exported. To allow this, the compiler automatically rewrites this to something like: ```ts - -class TypicalServer { - -} +class TypicalServer {} export const θ0 = () => new TypicalServer(); @NgModule({ - providers: [{provide: SERVER, useFactory: θ0}] + providers: [{provide: SERVER, useFactory: θ0}], }) export class TypicalModule {} - ``` This allows the compiler to generate a reference to `θ0` in the factory without having to know what the value of `θ0` contains. @@ -416,11 +402,11 @@ class MyComponent { This produces the following error: - +```shell {hideCopy} my.component.ts.MyComponent.html(1,1): : Property 'addresss' does not exist on type 'Person'. Did you mean 'address'? - +``` The file name reported in the error message, `my.component.ts.MyComponent.html`, is a synthetic file generated by the template compiler that holds contents of the `MyComponent` class template. @@ -435,11 +421,11 @@ location is the location of the attribute that contains the error. The validation uses the TypeScript type checker and the options supplied to the TypeScript compiler to control how detailed the type validation is. For example, if the `strictTypeChecks` is specified, the error - +```shell {hideCopy} my.component.ts.MyComponent.html(1,1): : Object is possibly 'undefined' - +``` is reported as well as the above error message. @@ -472,7 +458,7 @@ Use the non-null type assertion operator to suppress the `Object is possibly 'un In the following example, the `person` and `address` properties are always set together, implying that `address` is always non-null if `person` is non-null. There is no convenient way to describe this constraint to TypeScript and the template compiler, but the error is suppressed in the example by using `address!.street`. -```ts +```angular-ts @Component({ selector: 'my-component', @@ -494,7 +480,7 @@ The non-null assertion operator should be used sparingly as refactoring of the c In this example it is recommended to include the checking of `address` in the `*ngIf` as shown below: -```ts +```angular-ts @Component({ selector: 'my-component', diff --git a/adev/src/content/tools/cli/aot-metadata-errors.md b/adev/src/content/tools/cli/aot-metadata-errors.md index 945640b44a66..0f30278ec376 100644 --- a/adev/src/content/tools/cli/aot-metadata-errors.md +++ b/adev/src/content/tools/cli/aot-metadata-errors.md @@ -62,9 +62,7 @@ let foo = 42; // initialized The compiler will [fold](tools/cli/aot-compiler#code-folding) the expression into the provider as if you had written this. ```ts -providers: [ - { provide: Foo, useValue: 42 } -] +providers: [{provide: Foo, useValue: 42}]; ``` Alternatively, you can fix it by exporting `foo` with the expectation that `foo` will be assigned at runtime when you actually know its value. @@ -95,7 +93,7 @@ export let someTemplate: string; // exported but not initialized @Component({ selector: 'my-component', - template: someTemplate + template: someTemplate, }) export class MyComponent {} ``` @@ -119,7 +117,7 @@ export let someTemplate: string; @Component({ selector: 'my-component', - template: someTemplate + template: someTemplate, }) export class MyComponent {} ``` @@ -128,11 +126,11 @@ You'd also get this error if you imported `someTemplate` from some other module ```ts // ERROR - not initialized there either -import { someTemplate } from './config'; +import {someTemplate} from './config'; @Component({ selector: 'my-component', - template: someTemplate + template: someTemplate, }) export class MyComponent {} ``` @@ -148,7 +146,7 @@ export let someTemplate = '

    Greetings from Angular

    '; @Component({ selector: 'my-component', - template: someTemplate + template: someTemplate, }) export class MyComponent {} ``` @@ -378,14 +376,14 @@ This can happen if you use a number as a property name as in the following examp ```ts // ERROR -provider: [{ provide: Foo, useValue: { 0: 'test' } }] +provider: [{provide: Foo, useValue: {0: 'test'}}]; ``` Change the name of the property to something non-numeric. ```ts // CORRECTED -provider: [{ provide: Foo, useValue: { '0': 'test' } }] +provider: [{provide: Foo, useValue: {'0': 'test'}}]; ``` ## Unsupported enum member name diff --git a/adev/src/content/tools/cli/build-system-migration.md b/adev/src/content/tools/cli/build-system-migration.md index 51eec62be102..fcd33b7ab07c 100644 --- a/adev/src/content/tools/cli/build-system-migration.md +++ b/adev/src/content/tools/cli/build-system-migration.md @@ -370,7 +370,7 @@ console.log(contents); // ... Additionally, TypeScript needs to be aware of the module type for the import to prevent type-checking errors during the build. This can be accomplished with an additional type definition file within the application source code (`src/types.d.ts`, for example) with the following or similar content: ```ts -declare module "*.svg" { +declare module '*.svg' { const content: string; export default content; } @@ -402,7 +402,7 @@ As an example, an SVG file can be imported as text via: ```ts // @ts-expect-error TypeScript cannot provide types based on attributes yet -import contents from './some-file.svg' with { loader: 'text' }; +import contents from './some-file.svg' with {loader: 'text'}; ``` The same can be accomplished with an import expression inside an async function. @@ -410,7 +410,7 @@ The same can be accomplished with an import expression inside an async function. ```ts async function loadSvg(): Promise { // @ts-expect-error TypeScript cannot provide types based on attributes yet - return import('./some-file.svg', { with: { loader: 'text' } }).then((m) => m.default); + return import('./some-file.svg', {with: {loader: 'text'}}).then((m) => m.default); } ``` @@ -421,7 +421,7 @@ The `file` loader is useful when a file will be loaded at runtime through either ```ts // @ts-expect-error TypeScript cannot provide types based on attributes yet -import imagePath from './image.webp' with { loader: 'file' }; +import imagePath from './image.webp' with {loader: 'file'}; console.log(imagePath); // media/image-ULK2SIIB.webp ``` @@ -430,18 +430,18 @@ The `base64` loader is useful when a file needs to be embedded directly into the ```ts // @ts-expect-error TypeScript cannot provide types based on attributes yet -import logo from './logo.png' with { loader: 'base64' }; +import logo from './logo.png' with {loader: 'base64'}; -console.log(logo) // "iVBORw0KGgoAAAANSUhEUgAA..." +console.log(logo); // "iVBORw0KGgoAAAANSUhEUgAA..." ``` The `dataurl` loader to inline assets as complete Data URLs. ```ts // @ts-expect-error TypeScript cannot provide types based on attributes yet -import icon from './icon.svg' with { loader: 'dataurl' }; +import icon from './icon.svg' with {loader: 'dataurl'}; -console.log(icon);// "data:image/svg+xml;..." +console.log(icon); // "data:image/svg+xml;..." ``` For production builds as shown in the code comment above, hashing will be automatically added to the path for long-term caching. diff --git a/adev/src/content/tools/cli/cli-builder.md b/adev/src/content/tools/cli/cli-builder.md index cb9f1d053da2..782728231905 100644 --- a/adev/src/content/tools/cli/cli-builder.md +++ b/adev/src/content/tools/cli/cli-builder.md @@ -53,13 +53,13 @@ Builders can be published to `npm`, see [Publishing your Library](tools/librarie As an example, create a builder that copies a file to a new location. To create a builder, use the `createBuilder()` CLI Builder function, and return a `Promise` object. - + Now let's add some logic to it. The following code retrieves the source and destination file paths from user options and copies the file from the source to the destination \(using the [Promise version of the built-in NodeJS `copyFile()` function](https://nodejs.org/api/fs.html#fs_fspromises_copyfile_src_dest_mode)\). If the copy operation fails, it returns an error with a message about the underlying problem. - + ### Handling output @@ -70,7 +70,7 @@ This also lets the builder itself be executed in a separate process, even if the You can retrieve a `Logger` instance from the context. - + ### Progress and status reporting @@ -87,7 +87,7 @@ HELPFUL: There's no guarantee that a long string will be shown entirely; it coul Pass an empty string to remove the status. - + ## Builder input @@ -105,7 +105,6 @@ a `source` and a `destination`, each of which are a string. You can provide the following schema for type validation of these values. ```json {header: "schema.json"} - { "$schema": "http://json-schema.org/schema", "type": "object", @@ -128,7 +127,6 @@ To link our builder implementation with its schema and name, you need to create Create a file named `builders.json` that looks like this: ```json {header: "builders.json"} - { "builders": { "copy": { @@ -160,7 +158,7 @@ The first part of this is the package name and the second part is the builder na These values are accessed on `options.source` and `options.destination`. - + ### Target configuration @@ -302,27 +300,24 @@ This target tells the builder to copy the `package.json` file. - `source` - The existing file you are copying. - `destination` - The path you want to copy to. -< header="angular.json" language="json"> - +```json {header: "angular.json"} { -"projects": { -"builder-test": { -"architect": { -"copy-package": { -"builder": "@example/copy-file:copy", -"options": { -"source": "package.json", -"destination": "package-copy.json" -} -}, - + "projects": { + "builder-test": { + "architect": { + "copy-package": { + "builder": "@example/copy-file:copy", + "options": { + "source": "package.json", + "destination": "package-copy.json" + } + } // Existing targets... } } - -} + } } - +``` ### Running the builder diff --git a/adev/src/content/tools/cli/environments.md b/adev/src/content/tools/cli/environments.md index 7fe7b278cec1..902ef1069665 100644 --- a/adev/src/content/tools/cli/environments.md +++ b/adev/src/content/tools/cli/environments.md @@ -83,11 +83,9 @@ The base file `environment.ts`, contains the default environment settings. For example: ```ts - export const environment = { - production: true + production: true, }; - ``` The `build` command uses this as the build target when no environment is specified. @@ -95,24 +93,20 @@ You can add further variables, either as additional properties on the environmen For example, the following adds a default for a variable to the default environment: ```ts - export const environment = { production: true, - apiUrl: 'http://my-prod-url' + apiUrl: 'http://my-prod-url', }; - ``` You can add target-specific configuration files, such as `environment.development.ts`. The following content sets default values for the development build target: ```ts - export const environment = { production: false, - apiUrl: 'http://my-dev-url' + apiUrl: 'http://my-dev-url', }; - ``` ## Using environment-specific variables in your app @@ -120,9 +114,7 @@ export const environment = { To use the environment configurations you have defined, your components must import the original environments file: ```ts - -import { environment } from './environments/environment'; - +import {environment} from './environments/environment'; ``` This ensures that the build and serve commands can find the configurations for specific build targets. @@ -130,12 +122,10 @@ This ensures that the build and serve commands can find the configurations for s The following code in the component file (`app.component.ts`) uses an environment variable defined in the configuration files. ```ts - -import { environment } from './../environments/environment'; +import {environment} from './../environments/environment'; // Fetches from `http://my-prod-url` in production, `http://my-dev-url` in development. fetch(environment.apiUrl); - ``` The main CLI configuration file, `angular.json`, contains a `fileReplacements` section in the configuration for each build target, which lets you replace any file in the TypeScript program with a target-specific version of that file. diff --git a/adev/src/content/tools/cli/schematics-authoring.md b/adev/src/content/tools/cli/schematics-authoring.md index 4980bf7d8c2f..074e3968a9c8 100644 --- a/adev/src/content/tools/cli/schematics-authoring.md +++ b/adev/src/content/tools/cli/schematics-authoring.md @@ -36,19 +36,17 @@ A change can be accepted or ignored, or throw an exception. When you create a new blank schematic with the [Schematics CLI](#schematics-cli), the generated entry function is a _rule factory_. A `RuleFactory` object defines a higher-order function that creates a `Rule`. - - +```ts {header: "index.ts"} import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; // You don't have to export the function as default. // You can also have more than one rule factory per file. export function helloWorld(\_options: any): Rule { -return (tree: Tree,\_context: SchematicContext) => { -return tree; -}; + return (tree: Tree,\_context: SchematicContext) => { + return tree; + }; } - - +``` Your rules can make changes to your projects by calling external tools and implementing logic. You need a rule, for example, to define how a template in the schematic is to be merged into the hosting project. @@ -56,19 +54,17 @@ You need a rule, for example, to define how a template in the schematic is to be Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more. - - +```ts {header: "index.ts"} import { -JsonAstObject, -JsonObject, -JsonValue, -Path, -normalize, -parseJsonAst, -strings, + JsonAstObject, + JsonObject, + JsonValue, + Path, + normalize, + parseJsonAst, + strings, } from '@angular-devkit/core'; - - +``` ### Defining input options with a schema and interfaces @@ -157,19 +153,12 @@ In the short form, the type is inferred from the property's type and constraints In the following example, the property takes an enumerated value, so the schematic automatically chooses the list type, and creates a menu from the possible values. ```json {header: "schema.json"} - { "style": { "description": "The file extension or preprocessor to use for style files.", "type": "string", "default": "css", - "enum": [ - "css", - "scss", - "sass", - "less", - "styl" - ], + "enum": ["css", "scss", "sass", "less", "styl"], "x-prompt": "Which stylesheet format would you like to use?" } } @@ -195,26 +184,23 @@ It defines the prompt that lets users choose which style preprocessor they want By using the long form, the schematic can provide more explicit formatting of the menu choices. ```json {header: "schema.json"} - { "style": { "description": "The file extension or preprocessor to use for style files.", "type": "string", "default": "css", - "enum": [ - "css", - "scss", - "sass", - "less" - ], + "enum": ["css", "scss", "sass", "less"], "x-prompt": { "message": "Which stylesheet format would you like to use?", "type": "list", "items": [ - { "value": "css", "label": "CSS" }, - { "value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]" }, - { "value": "sass", "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" }, - { "value": "less", "label": "Less [ https://lesscss.org/ ]" } + {"value": "css", "label": "CSS"}, + {"value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]"}, + { + "value": "sass", + "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" + }, + {"value": "less", "label": "Less [ https://lesscss.org/ ]"} ] } } @@ -228,7 +214,6 @@ No additional logic or changes are required to the code of a schematic to suppor The following JSON schema is a complete description of the long-form syntax for the `x-prompt` field. ```json {header: "x-prompt schema"} - { "oneOf": [ { @@ -258,17 +243,13 @@ The following JSON schema is a complete description of the long-form syntax for }, "value": {} }, - "required": [ - "value" - ] + "required": ["value"] } ] } } }, - "required": [ - "message" - ] + "required": ["message"] } ] } @@ -363,10 +344,8 @@ The `src/` folder contains subfolders for named schematics in the collection, an Each schematic is created with a name, description, and factory function. ```json - { - "$schema": - "../node_modules/@angular-devkit/schematics/collection-schema.json", + "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "hello-world": { "description": "A blank schematic.", @@ -374,7 +353,6 @@ Each schematic is created with a name, description, and factory function. } } } - ``` - The `$schema` property specifies the schema that the CLI uses for validation. diff --git a/adev/src/content/tools/cli/schematics-for-libraries.md b/adev/src/content/tools/cli/schematics-for-libraries.md index 968dc1ce2574..beb6ce970dac 100644 --- a/adev/src/content/tools/cli/schematics-for-libraries.md +++ b/adev/src/content/tools/cli/schematics-for-libraries.md @@ -25,7 +25,7 @@ The following steps show you how to add initial support without modifying any pr 1. In your library project's `package.json` file, add a "schematics" entry with the path to your schema file. The Angular CLI uses this entry to find named schematics in your collection when it runs commands. - + The initial schema that you have created tells the CLI where to find the schematic that supports the `ng add` command. Now you are ready to create that schematic. @@ -47,7 +47,7 @@ The Angular CLI will install the latest version of the library automatically, an Use the `save` option of `ng-add` to configure if the library should be added to the `dependencies`, the `devDependencies`, or not saved at all in the project's `package.json` configuration file. - + Possible values are: @@ -107,7 +107,7 @@ When you add a schematic to the collection, you have to point to it in the colle 1. Edit the `schematics/collection.json` file to point to the new schematic subfolder, and include a pointer to a schema file that specifies inputs for the new schematic. - + 1. Go to the `/schematics/my-service` folder. 1. Create a `schema.json` file and define the available options for the schematic. @@ -142,19 +142,20 @@ Schematic templates support special syntax to execute code and variable substitu 1. Create a file named `__name@dasherize__.service.ts.template` that defines a template to use for generating files. This template will generate a service that already has Angular's `HttpClient` injected into an `http` property. - + ```ts {header:projects/my-lib/schematics/my-service/files/__name@dasherize__.service.ts.template (Schematic Template)} import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ - providedIn: 'root' + providedIn: 'root' }) export class <%= classify(name) %>Service { - private http = inject(HttpClient); + private http = inject(HttpClient); } - + ``` + - The `classify` and `dasherize` methods are utility functions that your schematic uses to transform your source template and filename. - The `name` is provided as a property from your factory function. It is the same `name` you defined in the schema. @@ -173,15 +174,15 @@ For details of these data structures and syntax, see the [Schematics README](htt 1. First, import the schematics definitions you will need. The Schematics framework offers many utility functions to create and use rules when running a schematic. - + 1. Import the defined schema interface that provides the type information for your schematic's options. - + 1. To build up the generation schematic, start with an empty rule factory. - + This rule factory returns the tree without modification. The options are the option values passed through from the `ng generate` command. @@ -203,13 +204,13 @@ The `Tree` methods give you access to the complete file tree in your workspace, To use `workspaces.readWorkspace` you need to create a `workspaces.WorkspaceHost` from the `Tree`. Add the following code to your factory function. - + Be sure to check that the context exists and throw the appropriate error. 1. Now that you have the project name, use it to retrieve the project-specific configuration information. - + The `workspace.projects` object contains all the project-specific configuration information. @@ -218,7 +219,7 @@ The `Tree` methods give you access to the complete file tree in your workspace, The `path` option in the schematic's schema is substituted by default with the current working directory. If the `path` is not defined, use the `sourceRoot` from the project configuration along with the `projectType`. - + ### Define the rule @@ -227,7 +228,7 @@ Use the templating to generate any custom files required for your schematic. 1. Add the following code to your factory function. - + | Methods | Details | | :----------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -240,7 +241,7 @@ Use the templating to generate any custom files required for your schematic. 1. Finally, the rule factory must return a rule. - + The `chain()` method lets you combine multiple rules into a single rule, so that you can perform multiple operations in a single schematic. Here you are only merging the template rules with any code executed by the schematic. @@ -299,8 +300,8 @@ ng generate my-lib:my-service --name my-data In the console, you see that the schematic was run and the `my-data.service.ts` file was created in your application folder. - +```shell {hideCopy} CREATE src/app/my-data.service.ts (208 bytes) - +``` diff --git a/adev/src/content/tools/cli/serve.md b/adev/src/content/tools/cli/serve.md index 34cab1771ea5..86c100d31287 100644 --- a/adev/src/content/tools/cli/serve.md +++ b/adev/src/content/tools/cli/serve.md @@ -9,23 +9,25 @@ You can stop the server by pressing `Ctrl+C`. You can determine which builder is being used for a particular project by looking up the `serve` target for that project. ```json - { "projects": { "my-app": { "architect": { // `ng serve` invokes the Architect target named `serve`. "serve": { - "builder": "@angular/build:dev-server", + "builder": "@angular/build:dev-server" // ... }, - "build": { /* ... */ }, - "test": { /* ... */ } + "build": { + /* ... */ + }, + "test": { + /* ... */ + } } } } } - ``` ## Proxying to a backend server @@ -62,7 +64,6 @@ For example, to divert all calls for `http://localhost:4200/api` to a server run } } } - ``` 1. To run the development server with this proxy configuration, call `ng serve`. diff --git a/adev/src/content/tools/cli/template-typecheck.md b/adev/src/content/tools/cli/template-typecheck.md index b6340c808fab..298eb81a9ec1 100644 --- a/adev/src/content/tools/cli/template-typecheck.md +++ b/adev/src/content/tools/cli/template-typecheck.md @@ -63,7 +63,6 @@ The three modes of type-checking treat embedded views differently. Consider the following example. ```ts {header:"User interface"} - interface User { name: string; address: { @@ -71,16 +70,13 @@ interface User { state: string; }; } - ``` ```html -

    {{config.title}}

    City: {{user.address.city}}
    - ``` The `

    ` and the `` are in the `*ngFor` embedded view. @@ -197,18 +193,14 @@ There are two potential workarounds to the preceding issues: - In the template, include the non-null assertion operator `!` at the end of a nullable expression, such as ```html - - ``` In this example, the compiler disregards type incompatibilities in nullability, just as in TypeScript code. In the case of the `async` pipe, notice that the expression needs to be wrapped in parentheses, as in ```html - - ``` - Disable strict null checks in Angular templates completely. @@ -252,17 +244,13 @@ All of this works as expected, as long as a `boolean` value is bound to the inpu But, suppose a consumer uses this input in the template as an attribute: ```html - - ``` This has the same effect as the binding: ```html - - ``` At runtime, the input will be set to the empty string, which is not a `boolean` value. @@ -285,7 +273,6 @@ As a workaround for this problem, Angular supports checking a wider, more permis Enable this by adding a static property with the `ngAcceptInputType_` prefix to the component class: ```ts - class SubmitButton { private _disabled: boolean; @@ -295,12 +282,11 @@ class SubmitButton { } set disabled(value: boolean) { - this._disabled = (value === '') || value; + this._disabled = value === '' || value; } - static ngAcceptInputType_disabled: boolean|''; + static ngAcceptInputType_disabled: boolean | ''; } - ``` Since TypeScript 4.3, the setter could have been declared to accept `boolean|''` as type, making the input setter coercion field obsolete. diff --git a/adev/src/content/tools/language-service.md b/adev/src/content/tools/language-service.md index c7b18d35b31f..58d9aba8e695 100644 --- a/adev/src/content/tools/language-service.md +++ b/adev/src/content/tools/language-service.md @@ -91,19 +91,17 @@ Starting with TypeScript 2.3, TypeScript has a plug-in model that the language s 1. Install the latest version of TypeScript in a local `node_modules` directory: -```shell - -npm install --save-dev typescript - -``` + ```shell + npm install --save-dev typescript + ``` 1. Install the Angular Language Service package in the same location: -```shell + ```shell -npm install --save-dev @angular/language-service + npm install --save-dev @angular/language-service -``` + ``` 1. Once the package is installed, add the following to the `"compilerOptions"` section of your project's `tsconfig.json`. @@ -113,7 +111,7 @@ npm install --save-dev @angular/language-service ] ``` -2. In your editor's user preferences \(`Cmd+,` or `Ctrl+,`\), add the following: +1. In your editor's user preferences \(`Cmd+,` or `Ctrl+,`\), add the following: ```json {header:"Sublime Text user preferences"} diff --git a/adev/src/content/tools/libraries/angular-package-format.md b/adev/src/content/tools/libraries/angular-package-format.md index 4176aa328288..41369683a99b 100644 --- a/adev/src/content/tools/libraries/angular-package-format.md +++ b/adev/src/content/tools/libraries/angular-package-format.md @@ -29,13 +29,13 @@ node_modules/@angular/core ├── README.md ├── package.json ├── fesm2022 -│ ├── core.mjs -│ ├── core.mjs.map -│ ├── testing.mjs -│ └── testing.mjs.map +│ ├── core.mjs +│ ├── core.mjs.map +│ ├── testing.mjs +│ └── testing.mjs.map └── types -│ ├── core.d.ts -│ ├── testing.d.ts +│ ├── core.d.ts +│ ├── testing.d.ts ``` This table describes the file layout under `node_modules/@angular/core` annotated to describe the purpose of files and directories: @@ -169,14 +169,9 @@ The README file in the Markdown format that is used to display description of a Example README content of @angular/core package: ```html - -Angular -======= - -The sources for this package are in the main [Angular](https://github.com/angular/angular) repo.Please file issues and pull requests against that repo. - -License: MIT - +Angular ======= The sources for this package are in +the main [Angular](https://github.com/angular/angular) repo.Please file issues and pull requests +against that repo. License: MIT ``` ## Partial compilation diff --git a/adev/src/content/tools/libraries/creating-libraries.md b/adev/src/content/tools/libraries/creating-libraries.md index 349f42c4fef2..bc33665ef9bd 100644 --- a/adev/src/content/tools/libraries/creating-libraries.md +++ b/adev/src/content/tools/libraries/creating-libraries.md @@ -196,7 +196,7 @@ To use your own library in an application: - In your applications, import from the library by name: ```ts - import { myExport } from 'my-lib'; +import {myExport} from 'my-lib'; ``` ### Building and rebuilding your library @@ -241,7 +241,7 @@ TypeScript path mappings should _not_ point to the library source `.ts` files. ### Linking libraries for local development This section explains how to use your package manager's local linking feature -(such as [`npm link`](https://pnpm.io/cli/link) or [`pnpm link`](https://pnpm.io/cli/link)) to test a standalone Angular library with an external application during +(such as [`npm link`](https://docs.npmjs.com/cli/v11/commands/npm-link) or [`pnpm link`](https://pnpm.io/cli/link)) to test a standalone Angular library with an external application during local development, without relying on the monorepo workspace structure or publishing to the NPM registry. NOTE: If your library and application are in the same Angular workspace (a monorepo setup), the standard monorepo workflow automatically handles the linking and is generally more efficient. This local linking approach is best when: @@ -277,9 +277,7 @@ To use linked libraries, you need to configure your application's `angular.json` "builder": "@angular-devkit/build-angular:dev-server", "options": { "prebundle": { - "exclude": [ - "my-lib" - ] + "exclude": ["my-lib"] } } } @@ -336,6 +334,6 @@ The Angular linker Babel plugin supports build caching, meaning that libraries o Example of integrating the plugin into a custom [webpack](https://webpack.js.org) build by registering the linker as a [Babel](https://babeljs.io) plugin using [babel-loader](https://webpack.js.org/loaders/babel-loader/#options). - + HELPFUL: The Angular CLI integrates the linker plugin automatically, so if consumers of your library are using the CLI, they can install Ivy-native libraries from npm without any additional configuration. diff --git a/adev/src/content/tools/libraries/using-libraries.md b/adev/src/content/tools/libraries/using-libraries.md index b1969d36b704..67f6db62a740 100644 --- a/adev/src/content/tools/libraries/using-libraries.md +++ b/adev/src/content/tools/libraries/using-libraries.md @@ -41,26 +41,24 @@ To do this: 1. Add the following code in `src/typings.d.ts`: -```ts -declare module 'host' { - export interface Host { - protocol?: string; - hostname?: string; - pathname?: string; - } - export function parse(url: string, queryString?: string): Host; -} - -``` + ```ts + declare module 'host' { + export interface Host { + protocol?: string; + hostname?: string; + pathname?: string; + } + export function parse(url: string, queryString?: string): Host; + } + ``` 1. In the component or file that uses the library, add the following code: -```ts -import * as host from 'host'; -const parsedUrl = host.parse('https://angular.dev'); -console.log(parsedUrl.hostname); - -``` + ```ts + import * as host from 'host'; + const parsedUrl = host.parse('https://angular.dev'); + console.log(parsedUrl.hostname); + ``` Define more typings as needed. @@ -85,33 +83,30 @@ For example, to use the [Bootstrap 4][GetbootstrapDocs40GettingStartedIntroducti 1. Install the library and the associated dependencies using the npm package manager: -```shell -npm install jquery --save -npm install popper.js --save -npm install bootstrap --save - -``` + ```shell + npm install jquery --save + npm install popper.js --save + npm install bootstrap --save + ``` 1. In the `angular.json` configuration file, add the associated script files to the `scripts` array: -```json -"scripts": [ - "node_modules/jquery/dist/jquery.slim.js", - "node_modules/popper.js/dist/umd/popper.js", - "node_modules/bootstrap/dist/js/bootstrap.js" -], - -``` + ```json + "scripts": [ + "node_modules/jquery/dist/jquery.slim.js", + "node_modules/popper.js/dist/umd/popper.js", + "node_modules/bootstrap/dist/js/bootstrap.js" + ], + ``` 1. Add the `bootstrap.css` CSS file to the `styles` array: -```json -"styles": [ - "node_modules/bootstrap/dist/css/bootstrap.css", - "src/styles.css" -], - -``` + ```json + "styles": [ + "node_modules/bootstrap/dist/css/bootstrap.css", + "src/styles.css" + ], + ``` 1. Run or restart the `ng serve` Angular CLI command to see Bootstrap 4 work in your application. @@ -121,9 +116,7 @@ After you import a library using the "scripts" array, do **not** import it using The following code snippet is an example import statement. ```ts - import * as $ from 'jquery'; - ``` If you import it using import statements, you have two different copies of the library: one imported as a global library, and one imported as a module. @@ -139,28 +132,22 @@ If the global library you need to use does not have global typings, you can decl For example: ```ts - declare var libraryName: any; - ``` Some scripts extend other libraries; for instance with JQuery plugins: ```ts - $('.test').myPlugin(); - ``` In this case, the installed `@types/jquery` does not include `myPlugin`, so you need to add an interface in `src/typings.d.ts`. For example: ```ts - interface JQuery { myPlugin(options?: any): any; } - ``` If you do not add the interface for the script-defined extension, your IDE shows an error: @@ -176,7 +163,7 @@ If you do not add the interface for the script-defined extension, your IDE shows [GuideWorkspaceConfig]: reference/configs/workspace-config 'Angular workspace configuration | Angular' [Resources]: resources 'Explore Angular Resources | Angular' [AngularMaterialMain]: https://material.angular.dev 'Angular Material | Angular' -[AngularUpdateMain]: https://angular.dev/update-guide 'Angular Update Guide | Angular' +[AngularUpdateMain]: /update-guide 'Angular Update Guide | Angular' [GetbootstrapDocs40GettingStartedIntroduction]: https://getbootstrap.com/docs/4.0/getting-started/introduction 'Introduction | Bootstrap' [NpmjsMain]: https://www.npmjs.com 'npm' [YarnpkgMain]: https://yarnpkg.com ' Yarn' diff --git a/adev/src/content/tutorials/deferrable-views/common/package-lock.json b/adev/src/content/tutorials/deferrable-views/common/package-lock.json index 48137ad6f816..daa68c844149 100644 --- a/adev/src/content/tutorials/deferrable-views/common/package-lock.json +++ b/adev/src/content/tutorials/deferrable-views/common/package-lock.json @@ -2363,15 +2363,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -2393,11 +2393,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -2446,25 +2446,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2527,16 +2517,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2614,16 +2594,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3603,6 +3573,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3862,9 +3842,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4022,11 +4002,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4494,9 +4474,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4725,19 +4705,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4840,9 +4821,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4854,7 +4835,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5072,11 +5057,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5654,16 +5639,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6084,16 +6059,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6185,16 +6150,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6209,16 +6164,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6255,16 +6200,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6417,16 +6352,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6539,11 +6464,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6639,13 +6564,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/deferrable-views/intro/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/intro/src/app/app.ts index 6b43cd5dc499..7b25d54b5e53 100644 --- a/adev/src/content/tutorials/deferrable-views/intro/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/intro/src/app/app.ts @@ -2,8 +2,6 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` - Welcome to Angular! - `, + template: ` Welcome to Angular! `, }) export class App {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/README.md b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/README.md index 97abbd37b20f..7d1dc52881c0 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/README.md +++ b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/README.md @@ -12,24 +12,24 @@ In this activity, you'll learn how to use deferrable views to defer load a secti In your `app.ts`, wrap the `article-comments` component with a `@defer` block to defer load it. - +```angular-html @defer { } - +``` By default, `@defer` loads the `article-comments` component when the browser is idle. In your browser's developer console, you can see that the `article-comments-component` lazy chunk file is loaded separately (The specific file names may change from run to run): - -Initial chunk files | Names | Raw size -chunk-NNSQHFIE.js | - | 769.00 kB | -main.js | main | 229.25 kB | +```markdown +Initial chunk files | Names | Raw size +chunk-NNSQHFIE.js | - | 769.00 kB | +main.js | main | 229.25 kB | Lazy chunk files | Names | Raw size chunk-T5UYXUSI.js | article-comments-component | 1.84 kB | - +``` diff --git a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/app.ts index aa67bfde28dd..bf743e510804 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/app.ts @@ -8,18 +8,15 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    @defer { } -

    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/answer/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/app.ts index 1d01152fbf4e..f76bdc6f6b10 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/app.ts @@ -8,16 +8,13 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    -
    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/README.md b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/README.md index 9d32e42dc251..40d584c770bb 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/README.md +++ b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/README.md @@ -39,25 +39,27 @@ In this activity, you'll learn how to use the `@loading`, `@error` and `@placeho In your `app.ts`, add a `@placeholder` block to the `@defer` block. - +```angular-html {highlight:[3,4,5]} @defer { } @placeholder {

    Placeholder for comments

    } -
    +``` +
    The `@placeholder` block accepts an optional parameter to specify the `minimum` amount of time that this placeholder should be shown. This `minimum` parameter is specified in time increments of milliseconds (ms) or seconds (s). This parameter exists to prevent fast flickering of placeholder content in the case that the deferred dependencies are fetched quickly. - +```angular-html {highlight:[3,4,5]} @defer { } @placeholder (minimum 1s) {

    Placeholder for comments

    } -
    +``` +
    @@ -72,7 +74,7 @@ Both parameters are specified in time increments of milliseconds (ms) or seconds Update `app.ts` to include a `@loading` block with a minimum parameter of `1s` as well as an after parameter with the value 500ms to the @loading block. - +```angular-html {highlight:[5,6,7]} @defer { } @placeholder (minimum 1s) { @@ -80,7 +82,7 @@ Update `app.ts` to include a `@loading` block with a minimum parameter of `1s` a } @loading (minimum 1s; after 500ms) {

    Loading comments...

    } -
    +``` NOTE: this example uses two parameters, separated by the ; character. @@ -89,7 +91,7 @@ NOTE: this example uses two parameters, separated by the ; character. Finally, add an `@error` block to the `@defer` block. - +```angular-html {highlight:[7,8,9]} @defer { } @placeholder (minimum 1s) { @@ -99,7 +101,8 @@ Finally, add an `@error` block to the `@defer` block. } @error {

    Failed to load comments

    } -
    +``` +
    diff --git a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/app.ts index 010d534fbf6a..e85192fb0220 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/app.ts @@ -8,11 +8,9 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    @@ -25,7 +23,6 @@ import {ArticleComments} from './article-comments'; } @error {

    Failed to load comments

    } -
    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/answer/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/app.ts index aa67bfde28dd..bf743e510804 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/app.ts @@ -8,18 +8,15 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    @defer { } -
    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/README.md b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/README.md index 4308728f5998..ec65aa8840cc 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/README.md +++ b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/README.md @@ -41,7 +41,7 @@ In this activity, you'll learn how to use triggers to specify the condition to l In your `app.ts`, add an `on hover` trigger to the `@defer` block. - +```angular-html {highlight:[1]} @defer (on hover) { } @placeholder (minimum 1s) { @@ -51,7 +51,7 @@ In your `app.ts`, add an `on hover` trigger to the `@defer` block. } @error {

    Failed to load comments

    } -
    +``` Now, the page will not render the comments section until you hover its placeholder.
    @@ -59,7 +59,7 @@ Now, the page will not render the comments section until you hover its placehold Next, update the template to include a button with the label "Show all comments". Include a template variable called `#showComments` with the button. - +```angular-html {highlight:[1]} @defer (on hover) { @@ -72,16 +72,16 @@ Next, update the template to include a button with the label "Show all comments" } @error {

    Failed to load comments

    } -
    +``` -NOTE: for more information on [template variables check the documentation](https://angular.dev/guide/templates/reference-variables#). +NOTE: for more information on [template variables check the documentation](/guide/templates/reference-variables).
    Update the `@defer` block in the template to use the `on interaction` trigger. Provide the `showComments` template variable as the parameter to `interaction`. - +```angular-html {highlight:[3]} @defer (on hover; on interaction(showComments)) { @@ -94,7 +94,7 @@ Update the `@defer` block in the template to use the `on interaction` trigger. P } @error {

    Failed to load comments

    } -
    +``` With these changes, the page will wait for one of the following conditions before rendering the comments section: @@ -105,5 +105,5 @@ You can reload the page to try out different triggers to render the comments sec
    -If you would like to learn more, check out the documentation for [Deferrable View](https://angular.dev/guide/defer). +If you would like to learn more, check out the documentation for [Deferrable View](/guide/defer). Keep learning to unlock more of Angular's great features. diff --git a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/app.ts index fbd6bdfd3545..74111779b1f9 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/app.ts @@ -8,11 +8,9 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    @@ -27,7 +25,6 @@ import {ArticleComments} from './article-comments'; } @error {

    Failed to load comments

    } -
    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/answer/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/app.ts b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/app.ts index 010d534fbf6a..e85192fb0220 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/app.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/app.ts @@ -8,11 +8,9 @@ import {ArticleComments} from './article-comments';

    How I feel about Angular

    - Angular is my favorite framework, and - this is why. Angular has the coolest - deferrable view feature that makes defer - loading content the easiest and most - ergonomic it could possibly be. + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly + be.

    @@ -25,7 +23,6 @@ import {ArticleComments} from './article-comments'; } @error {

    Failed to load comments

    } -
    `, imports: [ArticleComments], diff --git a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/article-comments.ts b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/article-comments.ts index 8d84a38a547a..f0d82f504688 100644 --- a/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/article-comments.ts +++ b/adev/src/content/tutorials/deferrable-views/steps/3-defer-triggers/src/app/article-comments.ts @@ -4,25 +4,19 @@ import {Component} from '@angular/core'; selector: 'article-comments', template: `

    Comments

    -

    - Building for the web is fantastic! -

    -

    - The new template syntax is great -

    -

    - I agree with the other comments! -

    +

    Building for the web is fantastic!

    +

    The new template syntax is great

    +

    I agree with the other comments!

    `, styles: [ ` - .comment { - padding: 15px; - margin-left: 30px; - background-color: paleturquoise; - border-radius: 20px; - } - `, + .comment { + padding: 15px; + margin-left: 30px; + background-color: paleturquoise; + border-radius: 20px; + } + `, ], }) export class ArticleComments {} diff --git a/adev/src/content/tutorials/first-app/common/package-lock.json b/adev/src/content/tutorials/first-app/common/package-lock.json index 7e445d9fd592..a7c5dafd642f 100644 --- a/adev/src/content/tutorials/first-app/common/package-lock.json +++ b/adev/src/content/tutorials/first-app/common/package-lock.json @@ -413,9 +413,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", - "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -430,9 +430,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/android-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", - "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -447,9 +447,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/android-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", - "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -464,9 +464,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/android-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", - "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -481,9 +481,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", - "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/darwin-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", - "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -515,9 +515,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", - "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -532,9 +532,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", - "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -549,9 +549,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", - "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -566,9 +566,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", - "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -583,9 +583,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", - "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -600,9 +600,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-loong64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", - "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -617,9 +617,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", - "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -634,9 +634,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", - "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -651,9 +651,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", - "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -668,9 +668,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-s390x": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", - "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -685,9 +685,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/linux-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", - "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -702,9 +702,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", - "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -719,9 +719,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", - "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -736,9 +736,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", - "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -753,9 +753,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", - "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", - "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], @@ -787,9 +787,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/sunos-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", - "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -804,9 +804,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/win32-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", - "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -821,9 +821,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/win32-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", - "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -838,9 +838,9 @@ } }, "node_modules/@angular/build/node_modules/@esbuild/win32-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", - "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -973,48 +973,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@angular/build/node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.0", - "@esbuild/android-arm": "0.27.0", - "@esbuild/android-arm64": "0.27.0", - "@esbuild/android-x64": "0.27.0", - "@esbuild/darwin-arm64": "0.27.0", - "@esbuild/darwin-x64": "0.27.0", - "@esbuild/freebsd-arm64": "0.27.0", - "@esbuild/freebsd-x64": "0.27.0", - "@esbuild/linux-arm": "0.27.0", - "@esbuild/linux-arm64": "0.27.0", - "@esbuild/linux-ia32": "0.27.0", - "@esbuild/linux-loong64": "0.27.0", - "@esbuild/linux-mips64el": "0.27.0", - "@esbuild/linux-ppc64": "0.27.0", - "@esbuild/linux-riscv64": "0.27.0", - "@esbuild/linux-s390x": "0.27.0", - "@esbuild/linux-x64": "0.27.0", - "@esbuild/netbsd-arm64": "0.27.0", - "@esbuild/netbsd-x64": "0.27.0", - "@esbuild/openbsd-arm64": "0.27.0", - "@esbuild/openbsd-x64": "0.27.0", - "@esbuild/openharmony-arm64": "0.27.0", - "@esbuild/sunos-x64": "0.27.0", - "@esbuild/win32-arm64": "0.27.0", - "@esbuild/win32-ia32": "0.27.0", - "@esbuild/win32-x64": "0.27.0" - } - }, "node_modules/@angular/build/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1128,491 +1086,523 @@ } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "node_modules/@angular/build/node_modules/vite/node_modules/esbuild": { "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], + "node_modules/@angular/build/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.0-next.0.tgz", + "integrity": "sha512-QpboTD5qCFjw1SRPjnVWJQ7c2s4Co5rnHSvVWuAixtM4rlNkulzgzcAndO4HqjnBq4Q8RRs8V1L/JeW71MSfwQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@angular-devkit/architect": "0.2101.0-next.0", + "@angular-devkit/core": "21.1.0-next.0", + "@angular-devkit/schematics": "21.1.0-next.0", + "@inquirer/prompts": "7.10.1", + "@listr2/prompt-adapter-inquirer": "3.0.5", + "@modelcontextprotocol/sdk": "1.22.0", + "@schematics/angular": "21.1.0-next.0", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.45.0", + "ini": "6.0.0", + "jsonc-parser": "3.3.1", + "listr2": "9.0.5", + "npm-package-arg": "13.0.2", + "pacote": "21.0.4", + "parse5-html-rewriting-stream": "8.0.0", + "resolve": "1.22.11", + "semver": "7.7.3", + "yargs": "18.0.0", + "zod": "3.25.76" + }, + "bin": { + "ng": "bin/ng.js" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], + "node_modules/@angular/cli/node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], + "node_modules/@angular/cli/node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], + "node_modules/@angular/cli/node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], + "node_modules/@angular/cli/node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli/node_modules/@listr2/prompt-adapter-inquirer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz", + "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@inquirer/type": "^3.0.8" + }, "engines": { - "node": ">=18" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 8", + "listr2": "9.0.5" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], + "node_modules/@angular/cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], + "node_modules/@angular/cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], + "node_modules/@angular/cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@angular/build/node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "node_modules/@angular/cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "node": ">=8" } }, - "node_modules/@angular/build/node_modules/wrap-ansi": { + "node_modules/@angular/cli/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", @@ -1627,950 +1617,918 @@ "node": ">=8" } }, - "node_modules/@angular/cli": { + "node_modules/@angular/common": { "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.0-next.0.tgz", - "integrity": "sha512-QpboTD5qCFjw1SRPjnVWJQ7c2s4Co5rnHSvVWuAixtM4rlNkulzgzcAndO4HqjnBq4Q8RRs8V1L/JeW71MSfwQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.0-next.0.tgz", + "integrity": "sha512-GS4D+A4qx5ojAdedUTjQXs3vfq9rzi8ffX1X/fTpycYL2j2CArOU/Tv2A6pedDlZetJMu0iifcLNuZNE4+ReKw==", "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2101.0-next.0", - "@angular-devkit/core": "21.1.0-next.0", - "@angular-devkit/schematics": "21.1.0-next.0", - "@inquirer/prompts": "7.10.1", - "@listr2/prompt-adapter-inquirer": "3.0.5", - "@modelcontextprotocol/sdk": "1.22.0", - "@schematics/angular": "21.1.0-next.0", - "@yarnpkg/lockfile": "1.1.0", - "algoliasearch": "5.45.0", - "ini": "6.0.0", - "jsonc-parser": "3.3.1", - "listr2": "9.0.5", - "npm-package-arg": "13.0.2", - "pacote": "21.0.4", - "parse5-html-rewriting-stream": "8.0.0", - "resolve": "1.22.11", - "semver": "7.7.3", - "yargs": "18.0.0", - "zod": "3.25.76" + "tslib": "^2.3.0" }, - "bin": { - "ng": "bin/ng.js" + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "21.1.0-next.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.0-next.0.tgz", + "integrity": "sha512-7RBOOws5enj6Cl63QPpFKEuXgnfUz+fVsSa2S772hVCarwPoV5p33hUYhSchGh54GkbU2lrQqyDXK6nUdUoWGw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/checkbox": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", - "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "node_modules/@angular/compiler-cli": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.0-next.0.tgz", + "integrity": "sha512-gaPkonFV0EPTiLLTcWC0hMYmlCYiG3R5TMxyaxqrMmcJrMqcI9eqJPqfD4zgnZqntS2IzJtzCx2PbZlj8eibpA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@babel/core": "7.28.5", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@types/node": ">=18" + "@angular/compiler": "21.1.0-next.0", + "typescript": ">=5.9 <6.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@angular/cli/node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", - "dev": true, + "node_modules/@angular/core": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.0-next.0.tgz", + "integrity": "sha512-dhAp5/QFwbtZC5ie8BGbLMkXeRu81WDdWPuNNBxq5qGRGixNsLQSO0fXDT5X1JK31mGbS+HEjxkv/BrLI7YH7w==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "tslib": "^2.3.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@types/node": ">=18" + "@angular/compiler": "21.1.0-next.0", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0 || ~0.16.0" }, "peerDependenciesMeta": { - "@types/node": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { "optional": true } } }, - "node_modules/@angular/cli/node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", - "dev": true, + "node_modules/@angular/forms": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.0-next.0.tgz", + "integrity": "sha512-lvrrag5KlRt+d4NgNb5JtkFF/AK8bBYcs921N6P5yTUlU1Lp/Ay3k2yiZlF4gjgDhNwp9xXt7Z/wx95lZNQvxQ==", "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" + "tslib": "^2.3.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "@angular/common": "21.1.0-next.0", + "@angular/core": "21.1.0-next.0", + "@angular/platform-browser": "21.1.0-next.0", + "@standard-schema/spec": "^1.0.0", + "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/editor": { - "version": "4.2.23", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", - "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", - "dev": true, + "node_modules/@angular/platform-browser": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.0-next.0.tgz", + "integrity": "sha512-ZyMQQ0C95FvH9MI72heIXXwUlo0lc6Mw2cTls03dOcjFcEX2vTqgrCXf5fjVNRWeVD5Nr5utGq5lFfmfRdzfZw==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/external-editor": "^1.0.3", - "@inquirer/type": "^3.0.10" + "tslib": "^2.3.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@types/node": ">=18" + "@angular/animations": "21.1.0-next.0", + "@angular/common": "21.1.0-next.0", + "@angular/core": "21.1.0-next.0" }, "peerDependenciesMeta": { - "@types/node": { + "@angular/animations": { "optional": true } } }, - "node_modules/@angular/cli/node_modules/@inquirer/expand": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", - "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", - "dev": true, + "node_modules/@angular/router": { + "version": "21.1.0-next.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.0-next.0.tgz", + "integrity": "sha512-tjSxC3bvOJu/yJ60Ccsma/zaX9AHO/cOxZLA8VHumO08apltWoC83vvxR6jPH8eH8FCvrLB3BKzSxjzLqH6vvQ==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "tslib": "^2.3.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "@angular/common": "21.1.0-next.0", + "@angular/core": "21.1.0-next.0", + "@angular/platform-browser": "21.1.0-next.0", + "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/input": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", - "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/number": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", - "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@angular/cli/node_modules/@inquirer/password": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", - "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@angular/cli/node_modules/@inquirer/prompts": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", - "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.3.2", - "@inquirer/confirm": "^5.1.21", - "@inquirer/editor": "^4.2.23", - "@inquirer/expand": "^4.0.23", - "@inquirer/input": "^4.3.1", - "@inquirer/number": "^3.0.23", - "@inquirer/password": "^4.0.23", - "@inquirer/rawlist": "^4.1.11", - "@inquirer/search": "^3.2.2", - "@inquirer/select": "^4.4.2" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/rawlist": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", - "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@babel/types": "^7.27.3" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/search": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", - "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/select": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", - "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/@listr2/prompt-adapter-inquirer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz", - "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/type": "^3.0.8" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { - "node": ">=20.0.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@inquirer/prompts": ">= 3 < 8", - "listr2": "9.0.5" + "@babel/core": "^7.0.0" } }, - "node_modules/@angular/cli/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "undici-types": "~7.16.0" + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@angular/cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/@angular/cli/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@angular/common": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.0-next.0.tgz", - "integrity": "sha512-GS4D+A4qx5ojAdedUTjQXs3vfq9rzi8ffX1X/fTpycYL2j2CArOU/Tv2A6pedDlZetJMu0iifcLNuZNE4+ReKw==", + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/core": "21.1.0-next.0", - "rxjs": "^6.5.3 || ^7.4.0" + "node": ">=6.9.0" } }, - "node_modules/@angular/compiler": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.0-next.0.tgz", - "integrity": "sha512-7RBOOws5enj6Cl63QPpFKEuXgnfUz+fVsSa2S772hVCarwPoV5p33hUYhSchGh54GkbU2lrQqyDXK6nUdUoWGw==", + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": ">=6.9.0" } }, - "node_modules/@angular/compiler-cli": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.0-next.0.tgz", - "integrity": "sha512-gaPkonFV0EPTiLLTcWC0hMYmlCYiG3R5TMxyaxqrMmcJrMqcI9eqJPqfD4zgnZqntS2IzJtzCx2PbZlj8eibpA==", + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@babel/core": "7.28.5", - "@jridgewell/sourcemap-codec": "^1.4.14", - "chokidar": "^4.0.0", - "convert-source-map": "^1.5.1", - "reflect-metadata": "^0.2.0", - "semver": "^7.0.0", - "tslib": "^2.3.0", - "yargs": "^18.0.0" - }, - "bin": { - "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.1.0-next.0", - "typescript": ">=5.9 <6.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" } }, - "node_modules/@angular/core": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.0-next.0.tgz", - "integrity": "sha512-dhAp5/QFwbtZC5ie8BGbLMkXeRu81WDdWPuNNBxq5qGRGixNsLQSO0fXDT5X1JK31mGbS+HEjxkv/BrLI7YH7w==", + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.1.0-next.0", - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.15.0 || ~0.16.0" - }, - "peerDependenciesMeta": { - "@angular/compiler": { - "optional": true - }, - "zone.js": { - "optional": true - } + "tslib": "^2.4.0" } }, - "node_modules/@angular/forms": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.0-next.0.tgz", - "integrity": "sha512-lvrrag5KlRt+d4NgNb5JtkFF/AK8bBYcs921N6P5yTUlU1Lp/Ay3k2yiZlF4gjgDhNwp9xXt7Z/wx95lZNQvxQ==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/common": "21.1.0-next.0", - "@angular/core": "21.1.0-next.0", - "@angular/platform-browser": "21.1.0-next.0", - "@standard-schema/spec": "^1.0.0", - "rxjs": "^6.5.3 || ^7.4.0" + "tslib": "^2.4.0" } }, - "node_modules/@angular/platform-browser": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.0-next.0.tgz", - "integrity": "sha512-ZyMQQ0C95FvH9MI72heIXXwUlo0lc6Mw2cTls03dOcjFcEX2vTqgrCXf5fjVNRWeVD5Nr5utGq5lFfmfRdzfZw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/animations": "21.1.0-next.0", - "@angular/common": "21.1.0-next.0", - "@angular/core": "21.1.0-next.0" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@angular/router": { - "version": "21.1.0-next.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.0-next.0.tgz", - "integrity": "sha512-tjSxC3bvOJu/yJ60Ccsma/zaX9AHO/cOxZLA8VHumO08apltWoC83vvxR6jPH8eH8FCvrLB3BKzSxjzLqH6vvQ==", + "node_modules/@esbuild/android-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/common": "21.1.0-next.0", - "@angular/core": "21.1.0-next.0", - "@angular/platform-browser": "21.1.0-next.0", - "rxjs": "^6.5.3 || ^7.4.0" + "node": ">=18" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "node_modules/@esbuild/android-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=18" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "node_modules/@esbuild/linux-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "node_modules/@esbuild/linux-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "debug": "^4.3.1" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "node_modules/@esbuild/win32-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@inquirer/ansi": { @@ -3228,15 +3186,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -3258,11 +3216,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -3311,25 +3269,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3392,16 +3340,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -3479,16 +3417,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -4468,6 +4396,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -4721,9 +4659,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4881,11 +4819,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5342,9 +5280,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -5466,6 +5404,48 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.0", + "@esbuild/android-arm": "0.27.0", + "@esbuild/android-arm64": "0.27.0", + "@esbuild/android-x64": "0.27.0", + "@esbuild/darwin-arm64": "0.27.0", + "@esbuild/darwin-x64": "0.27.0", + "@esbuild/freebsd-arm64": "0.27.0", + "@esbuild/freebsd-x64": "0.27.0", + "@esbuild/linux-arm": "0.27.0", + "@esbuild/linux-arm64": "0.27.0", + "@esbuild/linux-ia32": "0.27.0", + "@esbuild/linux-loong64": "0.27.0", + "@esbuild/linux-mips64el": "0.27.0", + "@esbuild/linux-ppc64": "0.27.0", + "@esbuild/linux-riscv64": "0.27.0", + "@esbuild/linux-s390x": "0.27.0", + "@esbuild/linux-x64": "0.27.0", + "@esbuild/netbsd-arm64": "0.27.0", + "@esbuild/netbsd-x64": "0.27.0", + "@esbuild/openbsd-arm64": "0.27.0", + "@esbuild/openbsd-x64": "0.27.0", + "@esbuild/openharmony-arm64": "0.27.0", + "@esbuild/sunos-x64": "0.27.0", + "@esbuild/win32-arm64": "0.27.0", + "@esbuild/win32-ia32": "0.27.0", + "@esbuild/win32-x64": "0.27.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -5531,19 +5511,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -5646,9 +5627,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -5660,7 +5641,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5878,11 +5863,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6388,16 +6373,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6818,16 +6793,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6919,16 +6884,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6943,16 +6898,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6989,16 +6934,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -7151,16 +7086,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -7273,11 +7198,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -7373,13 +7298,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/first-app/intro/src/app/app.ts b/adev/src/content/tutorials/first-app/intro/src/app/app.ts index 6b43cd5dc499..7b25d54b5e53 100644 --- a/adev/src/content/tutorials/first-app/intro/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/intro/src/app/app.ts @@ -2,8 +2,6 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` - Welcome to Angular! - `, + template: ` Welcome to Angular! `, }) export class App {} diff --git a/adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.ts index 59bc4cad7c69..58d7299b9c57 100644 --- a/adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.ts @@ -3,9 +3,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', imports: [], - template: ` -

    Default

    - `, + template: `

    Default

    `, styleUrls: ['./app.css'], }) export class App { diff --git a/adev/src/content/tutorials/first-app/steps/01-hello-world/src/index.html b/adev/src/content/tutorials/first-app/steps/01-hello-world/src/index.html index fb82dcc0fd8f..e69cedb60455 100644 --- a/adev/src/content/tutorials/first-app/steps/01-hello-world/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/01-hello-world/src/index.html @@ -1,14 +1,17 @@ - + - - - Default - - - - - - - - + + + Default + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/02-Home/README.md b/adev/src/content/tutorials/first-app/steps/02-Home/README.md index 9af6c5e5e074..155e8b19c152 100644 --- a/adev/src/content/tutorials/first-app/steps/02-Home/README.md +++ b/adev/src/content/tutorials/first-app/steps/02-Home/README.md @@ -63,26 +63,26 @@ In this step, you add the new component, `Home` to your app's root component, `A In the **Edit** pane of your IDE: -1. Open `app.ts` in the editor. -1. In `app.ts`, import `Home` by adding this line to the file level imports. +1. Open `app.ts` in the editor. +1. In `app.ts`, import `Home` by adding this line to the file level imports. - + -1. In `app.ts`, in `@Component`, update the `imports` array property and add `Home`. +1. In `app.ts`, in `@Component`, update the `imports` array property and add `Home`. - + -1. In `app.ts`, in `@Component`, update the `template` property to include the following HTML code. +1. In `app.ts`, in `@Component`, update the `template` property to include the following HTML code. - + -1. Save your changes to `app.ts`. -1. If `ng serve` is running, the app should update. - If `ng serve` is not running, start it again. - _Hello world_ in your app should change to _home works!_ from the `Home`. -1. Check the running app in the browser and confirm that the app has been updated. +1. Save your changes to `app.ts`. +1. If `ng serve` is running, the app should update. + If `ng serve` is not running, start it again. + _Hello world_ in your app should change to _home works!_ from the `Home`. +1. Check the running app in the browser and confirm that the app has been updated. -browser frame of page displaying the text 'home works!' + browser frame of page displaying the text 'home works!' @@ -95,24 +95,42 @@ In this step, you add a search filter and button that is used in a later lesson. For now, that's all that `Home` has. Note that, this step just adds the search elements to the layout without any functionality, yet. +If you started from a fresh Angular project instead of downloading the starter +(ng new): add these globals to `src/styles.css` so the search button and input border are visible: + +``` +:root { + --primary-color: #605DC8; + --secondary-color: #8B89E6; + --accent-color: #e8e7fa; + --shadow-color: #E8E8E8; +} + +button.primary { + padding: 10px; + border: solid 1px var(--primary-color); + background: var(--primary-color); + color: white; + border-radius: 8px; +} +``` + In the **Edit** pane of your IDE: -1. In the `first-app` directory, open `home.ts` in the editor. -1. In `home.ts`, in `@Component`, update the `template` property with this code. +1. In the `first-app` directory, open `home.ts` in the editor. +1. In `home.ts`, in `@Component`, update the `template` property with this code. - + -1. Next, open `home.css` in the editor and update the content with these styles. +1. Next, open `home.css` in the editor and update the content with these styles. - NOTE: In the browser, these can go in `src/app/home/home.ts` in the `styles` array. + NOTE: In the browser, these can go in `src/app/home/home.ts` in the `styles` array. - + -1. Confirm that the app builds without error. - You should find the filter query box and button in your app and they should be styled. - Correct any errors before you continue to the next step. +1. Confirm that the app builds without error. You should find the filter query box and button in your app and they should be styled. Correct any errors before you continue to the next step. -browser frame of homes-app displaying logo, filter text input box and search button + browser frame of homes-app displaying logo, filter text input box and search button diff --git a/adev/src/content/tutorials/first-app/steps/02-Home/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/02-Home/src/app/app.ts index 3e08b2d8c3c3..b00712110b00 100644 --- a/adev/src/content/tutorials/first-app/steps/02-Home/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/02-Home/src/app/app.ts @@ -3,9 +3,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', imports: [], - template: ` -

    Hello world!

    - `, + template: `

    Hello world!

    `, styleUrls: ['./app.css'], }) export class App { diff --git a/adev/src/content/tutorials/first-app/steps/02-Home/src/index.html b/adev/src/content/tutorials/first-app/steps/02-Home/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/02-Home/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/02-Home/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/README.md b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/README.md index d3c3ba6018c0..ac8d748b9f41 100644 --- a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/README.md +++ b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/README.md @@ -44,18 +44,18 @@ In this step, you add the new component, `HousingLocation` to your app's `Home`, In the **Edit** pane of your IDE: -1. Open `home.ts` in the editor. -1. In `home.ts`, import `HousingLocation` by adding this line to the file level imports. +1. Open `home.ts` in the editor. +1. In `home.ts`, import `HousingLocation` by adding this line to the file level imports. - + -1. Next update the `imports` property of the `@Component` metadata by adding `HousingLocation` to the array. +1. Next update the `imports` property of the `@Component` metadata by adding `HousingLocation` to the array. - + -1. Now the component is ready for use in the template for the `Home`. Update the `template` property of the `@Component` metadata to include a reference to the `` tag. +1. Now the component is ready for use in the template for the `Home`. Update the `template` property of the `@Component` metadata to include a reference to the `` tag. - + @@ -70,7 +70,7 @@ In this step, you will copy over the pre-written styles for the `HousingLocation 1. Save your code, return to the browser and confirm that the app builds without error. You should find the message "housing-location works!" rendered to the screen.Correct any errors before you continue to the next step. -browser frame of homes-app displaying logo, filter text input box and search button and the message 'housing-location works! + browser frame of homes-app displaying logo, filter text input box and search button and the message 'housing-location works! diff --git a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/index.html b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/03-HousingLocation/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/04-interfaces/README.md b/adev/src/content/tutorials/first-app/steps/04-interfaces/README.md index 5d9f39658135..3e47de772618 100644 --- a/adev/src/content/tutorials/first-app/steps/04-interfaces/README.md +++ b/adev/src/content/tutorials/first-app/steps/04-interfaces/README.md @@ -44,13 +44,13 @@ In the **Terminal** pane of your IDE: This step adds the properties to the interface that your app needs to represent a housing location. -1. In the **Terminal** pane of your IDE, start the `ng serve` command, if it isn't already running, to build the app and serve it to `http://localhost:4200`. -1. In the **Edit** pane of your IDE, open the `src/app/housinglocation.ts` file. -1. In `housinglocation.ts`, replace the default content with the following code to make your new interface to match this example. +1. In the **Terminal** pane of your IDE, start the `ng serve` command, if it isn't already running, to build the app and serve it to `http://localhost:4200`. +1. In the **Edit** pane of your IDE, open the `src/app/housinglocation.ts` file. +1. In `housinglocation.ts`, replace the default content with the following code to make your new interface to match this example. - + -1. Save your changes and confirm the app does not display any errors. Correct any errors before you continue to the next step. +1. Save your changes and confirm the app does not display any errors. Correct any errors before you continue to the next step. At this point, you've defined an interface that represents data about a housing location including an `id`, `name`, and location information. @@ -62,28 +62,28 @@ In this step, you create an instance of the interface and assign some sample dat You won't see this sample data appear in your app yet. There are a few more lessons to complete before that happens. -1. In the **Terminal** pane of your IDE, run the `ng serve` command, if it isn't already running, to build the app and serve your app to `http://localhost:4200`. -1. In the **Edit** pane of your IDE, open `src/app/home/home.ts`. -1. In `src/app/home/home.ts`, add this import statement after the existing `import` statements so that `Home` can use the new interface. +1. In the **Terminal** pane of your IDE, run the `ng serve` command, if it isn't already running, to build the app and serve your app to `http://localhost:4200`. +1. In the **Edit** pane of your IDE, open `src/app/home/home.ts`. +1. In `src/app/home/home.ts`, add this import statement after the existing `import` statements so that `Home` can use the new interface. - + -1. In `src/app/home/home.ts`, replace the empty `export class Home {}` definition with this code to create a single instance of the new interface in the component. +1. In `src/app/home/home.ts`, replace the empty `export class Home {}` definition with this code to create a single instance of the new interface in the component. - + -1. Confirm that your `home.ts` file matches this example. +1. Confirm that your `home.ts` file matches this example. - + - By adding the `housingLocation` property of type `HousingLocation` to the `Home` class, we're able to confirm that the data matches the description of the interface. If the data didn't satisfy the description of the interface, the IDE has enough information to give us helpful errors. + By adding the `housingLocation` property of type `HousingLocation` to the `Home` class, we're able to confirm that the data matches the description of the interface. If the data didn't satisfy the description of the interface, the IDE has enough information to give us helpful errors. -1. Save your changes and confirm the app does not have any errors. Open the browser and confirm that your application still displays the message "housing-location works!" +1. Save your changes and confirm the app does not have any errors. Open the browser and confirm that your application still displays the message "housing-location works!" -browser frame of homes-app displaying logo, filter text input box and search button and the message 'housing-location works!' + browser frame of homes-app displaying logo, filter text input box and search button and the message 'housing-location works!' -1. Correct any errors before you continue to the next step. - +1. Correct any errors before you continue to the next step. + diff --git a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/housing-location/housing-location.ts b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/housing-location/housing-location.ts index 2402a40c973b..c7e7de9b224d 100644 --- a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/housing-location/housing-location.ts +++ b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/app/housing-location/housing-location.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-housing-location', - template: ` -

    housing-location works!

    - `, + template: `

    housing-location works!

    `, styleUrls: ['./housing-location.css'], }) export class HousingLocation {} diff --git a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/index.html b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/04-interfaces/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/04-interfaces/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/housing-location/housing-location.ts b/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/housing-location/housing-location.ts index 2402a40c973b..c7e7de9b224d 100644 --- a/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/housing-location/housing-location.ts +++ b/adev/src/content/tutorials/first-app/steps/05-inputs/src/app/housing-location/housing-location.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-housing-location', - template: ` -

    housing-location works!

    - `, + template: `

    housing-location works!

    `, styleUrls: ['./housing-location.css'], }) export class HousingLocation {} diff --git a/adev/src/content/tutorials/first-app/steps/05-inputs/src/index.html b/adev/src/content/tutorials/first-app/steps/05-inputs/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/05-inputs/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/05-inputs/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/06-property-binding/README.md b/adev/src/content/tutorials/first-app/steps/06-property-binding/README.md index 0bf9b0a5e4e9..1821e58dfb10 100644 --- a/adev/src/content/tutorials/first-app/steps/06-property-binding/README.md +++ b/adev/src/content/tutorials/first-app/steps/06-property-binding/README.md @@ -28,11 +28,11 @@ In the code editor: 1. In the template property of the `@Component` decorator, update the code to match the code below: - When adding a property binding to a component tag, we use the `[attribute] = "value"` syntax to notify Angular that the assigned value should be treated as a property from the component class and not a string value. + When adding a property binding to a component tag, we use the `[attribute] = "value"` syntax to notify Angular that the assigned value should be treated as a property from the component class and not a string value. - The value on the right-hand side is the name of the property from the `Home`. + The value on the right-hand side is the name of the property from the `Home`. - + 1. Save your changes and confirm the app does not have any errors. diff --git a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/home/home.ts index 1d12afbf0054..e2f8657683c9 100644 --- a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/home/home.ts @@ -13,7 +13,7 @@ import {HousingLocationInfo} from '../housinglocation';
    - +
    `, styleUrls: ['./home.css'], diff --git a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/housing-location/housing-location.ts b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/housing-location/housing-location.ts index cac60fd0bac6..e01bdaa958bb 100644 --- a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/housing-location/housing-location.ts +++ b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/app/housing-location/housing-location.ts @@ -3,9 +3,7 @@ import {HousingLocationInfo} from '../housinglocation'; @Component({ selector: 'app-housing-location', - template: ` -

    housing-location works!

    - `, + template: `

    housing-location works!

    `, styleUrls: ['./housing-location.css'], }) export class HousingLocation { diff --git a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/index.html b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/06-property-binding/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/06-property-binding/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/README.md b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/README.md index a1654ec7b183..e09c85148816 100644 --- a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/README.md +++ b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/README.md @@ -27,7 +27,7 @@ In the code editor: 1. Navigate to `src/app/housing-location/housing-location.ts` 1. In the template property of the `@Component` decorator, replace the existing HTML markup with the following code: - + In this updated template code you have used property binding to bind the `housingLocation.photo` to the `src` attribute. The `alt` attribute uses interpolation to give more context to the alt text of the image. diff --git a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/housing-location/housing-location.ts b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/housing-location/housing-location.ts index cac60fd0bac6..e01bdaa958bb 100644 --- a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/housing-location/housing-location.ts +++ b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/app/housing-location/housing-location.ts @@ -3,9 +3,7 @@ import {HousingLocationInfo} from '../housinglocation'; @Component({ selector: 'app-housing-location', - template: ` -

    housing-location works!

    - `, + template: `

    housing-location works!

    `, styleUrls: ['./housing-location.css'], }) export class HousingLocation { diff --git a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/index.html b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/07-dynamic-template-values/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/08-ngFor/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/08-ngFor/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/08-ngFor/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/08-ngFor/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/08-ngFor/src/index.html b/adev/src/content/tutorials/first-app/steps/08-ngFor/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/08-ngFor/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/08-ngFor/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/09-services/README.md b/adev/src/content/tutorials/first-app/steps/09-services/README.md index ed8164017569..c797bb535b43 100644 --- a/adev/src/content/tutorials/first-app/steps/09-services/README.md +++ b/adev/src/content/tutorials/first-app/steps/09-services/README.md @@ -35,9 +35,9 @@ In the **Terminal** pane of your IDE: 1. In your project directory, navigate to the `first-app` directory. 1. In the `first-app` directory, run this command to create the new service. -```shell -ng generate service housing --skip-tests -``` + ```shell + ng generate service housing --skip-tests + ``` 1. Run `ng serve` to build the app and serve it to `http://localhost:4200`. 1. Confirm that the app builds without error. @@ -63,7 +63,7 @@ In the **Edit** pane of your IDE: 1. Add a file level import for the `HousingLocation`. - + 1. Confirm that the app builds without error. Correct any errors before you continue to the next step. @@ -75,23 +75,23 @@ In a later lesson, you'll replace the static data with a live data source to get In the **Edit** pane of your IDE, in `src/app/home/home.ts`: -1. At the top of `src/app/home/home.ts`, add the `inject` to the items imported from `@angular/core`. This will import the `inject` function into the `Home` class. +1. At the top of `src/app/home/home.ts`, add the `inject` to the items imported from `@angular/core`. This will import the `inject` function into the `Home` class. - + -1. Add a new file level import for the `HousingService`: +1. Add a new file level import for the `HousingService`: - + -1. From `Home`, delete the `housingLocationList` array entries and assign `housingLocationList` the value of empty array (`[]`). In a few steps you will update the code to pull the data from the `HousingService`. +1. From `Home`, delete the `housingLocationList` array entries and assign `housingLocationList` the value of empty array (`[]`). In a few steps you will update the code to pull the data from the `HousingService`. -1. In `Home`, add the following code to inject the new service and initialize the data for the app. The `constructor` is the first function that runs when this component is created. The code in the `constructor` will assign the `housingLocationList` the value returned from the call to `getAllHousingLocations`. +1. In `Home`, add the following code to inject the new service and initialize the data for the app. The `constructor` is the first function that runs when this component is created. The code in the `constructor` will assign the `housingLocationList` the value returned from the call to `getAllHousingLocations`. - + -1. Save the changes to `src/app/home/home.ts` and confirm your app builds without error. - Correct any errors before you continue to the next step. -
    +1. Save the changes to `src/app/home/home.ts` and confirm your app builds without error. + Correct any errors before you continue to the next step. + diff --git a/adev/src/content/tutorials/first-app/steps/09-services/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/09-services/src/app/app.ts index b67e16f7f684..24174f4e0130 100644 --- a/adev/src/content/tutorials/first-app/steps/09-services/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/09-services/src/app/app.ts @@ -10,7 +10,7 @@ import {Home} from './home/home';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.ts index c6cb4b0b5c08..4bdea1458da8 100644 --- a/adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/09-services/src/app/home/home.ts @@ -13,7 +13,7 @@ import {HousingLocationInfo} from '../housinglocation';
    - @for(housingLocation of housingLocationList; track $index) { + @for (housingLocation of housingLocationList; track $index) { }
    diff --git a/adev/src/content/tutorials/first-app/steps/09-services/src/index.html b/adev/src/content/tutorials/first-app/steps/09-services/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/09-services/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/09-services/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/10-routing/README.md b/adev/src/content/tutorials/first-app/steps/10-routing/README.md index b0e5efdb6675..726eb9781e36 100644 --- a/adev/src/content/tutorials/first-app/steps/10-routing/README.md +++ b/adev/src/content/tutorials/first-app/steps/10-routing/README.md @@ -34,24 +34,27 @@ In this lesson, you will enable routing in your application to navigate to the d 1. In the `src/app` directory, create a file called `routes.ts`. This file is where we will define the routes in the application. -2. In `main.ts`, make the following updates to enable routing in the application: - 1. Import the routes file and the `provideRouter` function: +2. In `main.ts`, make the following updates to enable routing in the application: + 1. Import the routes file and the `provideRouter` function: - - 2. Update the call to `bootstrapApplication` to include the routing configuration: + - + 1. Update the call to `bootstrapApplication` to include the routing configuration: -3. In `src/app/app.ts`, update the component to use routing: - 1. Add file level imports for the router directives `RouterOutlet` and `RouterLink`: + - - 2. Add `RouterOutlet` and `RouterLink` to the `@Component` metadata imports +3. In `src/app/app.ts`, update the component to use routing: + 1. Add file level imports for the router directives `RouterOutlet` and `RouterLink`: - - 3. In the `template` property, replace the `` tag with the `` directive and add a link back to the home page. Your code should match this code: + - + 1. Add `RouterOutlet` and `RouterLink` to the `@Component` metadata imports + + + + 1. In the `template` property, replace the `` tag with the `` directive and add a link back to the home page. Your code should match this code: + + @@ -61,11 +64,12 @@ In the previous step you removed the reference to the `` component in 1. In `routes.ts`, perform the following updates to create a route. 1. Add a file level imports for the `Home`, `Details` and the `Routes` type that you'll use in the route definitions. - + + 1. Define a variable called `routeConfig` of type `Routes` and define two routes for the app: - The entries in the `routeConfig` array represent the routes in the application. The first entry navigates to the `Home` whenever the url matches `''`. The second entry uses some special formatting that will be revisited in a future lesson. + The entries in the `routeConfig` array represent the routes in the application. The first entry navigates to the `Home` whenever the url matches `''`. The second entry uses some special formatting that will be revisited in a future lesson. 1. Save all changes and confirm that the application works in the browser. The application should still display the list of housing locations. diff --git a/adev/src/content/tutorials/first-app/steps/10-routing/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/10-routing/src/app/home/home.ts index 4ccd865f60a8..bc4ffb89e86f 100644 --- a/adev/src/content/tutorials/first-app/steps/10-routing/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/10-routing/src/app/home/home.ts @@ -13,7 +13,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of housingLocationList; track $index) { + @for (housingLocation of housingLocationList; track $index) { }
    diff --git a/adev/src/content/tutorials/first-app/steps/10-routing/src/index.html b/adev/src/content/tutorials/first-app/steps/10-routing/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/10-routing/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/10-routing/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/11-details-page/README.md b/adev/src/content/tutorials/first-app/steps/11-details-page/README.md index 8b92116fa9da..864ca99b8a77 100644 --- a/adev/src/content/tutorials/first-app/steps/11-details-page/README.md +++ b/adev/src/content/tutorials/first-app/steps/11-details-page/README.md @@ -27,51 +27,51 @@ In lesson 10, you added a second route to `src/app/routes.ts` which includes a s In this case, `:id` is dynamic and will change based on how the route is requested by the code. -1. In `src/app/housing-location/housing-location.ts`, add an anchor tag to the `section` element and include the `routerLink` directive: +1. In `src/app/housing-location/housing-location.ts`, add an anchor tag to the `section` element and include the `routerLink` directive: - + - The `routerLink` directive enables Angular's router to create dynamic links in the application. The value assigned to the `routerLink` is an array with two entries: the static portion of the path and the dynamic data. + The `routerLink` directive enables Angular's router to create dynamic links in the application. The value assigned to the `routerLink` is an array with two entries: the static portion of the path and the dynamic data. - For the `routerLink` to work in the template, add a file level import of `RouterLink` and `RouterOutlet` from '@angular/router', then update the component `imports` array to include both `RouterLink` and `RouterOutlet`. + For the `routerLink` to work in the template, add a file level import of `RouterLink` and `RouterOutlet` from '@angular/router', then update the component `imports` array to include both `RouterLink` and `RouterOutlet`. -1. At this point you can confirm that the routing is working in your app. In the browser, refresh the home page and click the "Learn More" button for a housing location. +1. At this point you can confirm that the routing is working in your app. In the browser, refresh the home page and click the "Learn More" button for a housing location. -details page displaying the text 'details works!' + details page displaying the text 'details works!' In this step, you will get the route parameter in the `Details`. Currently, the app displays `details works!`. Next you'll update the code to display the `id` value passed using the route parameters. -1. In `src/app/details/details.ts` update the template to import the functions, classes and services that you'll need to use in the `Details`: +1. In `src/app/details/details.ts` update the template to import the functions, classes and services that you'll need to use in the `Details`: - + -1. Update the `template` property of the `@Component` decorator to display the value `housingLocationId`: +1. Update the `template` property of the `@Component` decorator to display the value `housingLocationId`: - ```angular-ts - template: `

    details works! {{ housingLocationId }}

    `, - ``` + ```angular-ts + template: `

    details works! {{ housingLocationId }}

    `, + ``` -1. Update the body of the `Details` class with the following code: +1. Update the body of the `Details` class with the following code: - ```ts - export class Details { - route: ActivatedRoute = inject(ActivatedRoute); - housingLocationId = -1; - constructor() { - this.housingLocationId = Number(this.route.snapshot.params['id']); - } - } - ``` + ```ts + export class Details { + route: ActivatedRoute = inject(ActivatedRoute); + housingLocationId = -1; + constructor() { + this.housingLocationId = Number(this.route.snapshot.params['id']); + } + } + ``` - This code gives the `Details` access to the `ActivatedRoute` router feature that enables you to have access to the data about the current route. In the `constructor`, the code converts the `id` parameter acquired from the route from a string to a number. + This code gives the `Details` access to the `ActivatedRoute` router feature that enables you to have access to the data about the current route. In the `constructor`, the code converts the `id` parameter acquired from the route from a string to a number. -1. Save all changes. +1. Save all changes. -1. In the browser, click on one of the housing location's "Learn More" links and confirm that the numeric value displayed on the page matches the `id` property for that location in the data. -
    +1. In the browser, click on one of the housing location's "Learn More" links and confirm that the numeric value displayed on the page matches the `id` property for that location in the data. + Now that routing is working properly in the application this is a great time to update the template of the `Details` to display the specific data represented by the housing location for the route parameter. @@ -110,9 +110,9 @@ In a previous lesson you updated the `App` template to include a `routerLink`. A 1. Confirm that your code matches the following: - + - Your code should already be up-to-date but confirm to be sure. + Your code should already be up-to-date but confirm to be sure. diff --git a/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/details/details.ts b/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/details/details.ts index b5e2e953562a..af5d1bd51b98 100644 --- a/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/details/details.ts +++ b/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/details/details.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-details', - template: ` -

    details works!

    - `, + template: `

    details works!

    `, styleUrls: ['./details.css'], }) export class Details {} diff --git a/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/home/home.ts index 43a2b0002cf5..a0303bb1ebbd 100644 --- a/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/11-details-page/src/app/home/home.ts @@ -14,7 +14,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of housingLocationList; track $index) { + @for (housingLocation of housingLocationList; track $index) { }
    diff --git a/adev/src/content/tutorials/first-app/steps/11-details-page/src/index.html b/adev/src/content/tutorials/first-app/steps/11-details-page/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/11-details-page/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/11-details-page/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/12-forms/README.md b/adev/src/content/tutorials/first-app/steps/12-forms/README.md index 249b2c1b84c5..e6f26e5573a1 100644 --- a/adev/src/content/tutorials/first-app/steps/12-forms/README.md +++ b/adev/src/content/tutorials/first-app/steps/12-forms/README.md @@ -23,42 +23,42 @@ In this example, the method writes the data from the form to the browser's conso In the **Edit** pane of your IDE: -1. In `src/app/housing.service.ts`, inside the `HousingService` class, paste this method at the bottom of the class definition. +1. In `src/app/housing.service.ts`, inside the `HousingService` class, paste this method at the bottom of the class definition. - + -1. Confirm that the app builds without error. - Correct any errors before you continue to the next step. - +1. Confirm that the app builds without error. + Correct any errors before you continue to the next step. + This step adds the code to the details page that handles the form's interactions. In the **Edit** pane of your IDE, in `src/app/details/details.ts`: -1. After the `import` statements at the top of the file, add the following code to import the Angular form classes. +1. After the `import` statements at the top of the file, add the following code to import the Angular form classes. - + -1. In the `Details` decorator metadata, update the `imports` property with the following code: +1. In the `Details` decorator metadata, update the `imports` property with the following code: - + -1. In the `Details` class, before the `constructor()` method, add the following code to create the form object. +1. In the `Details` class, before the `constructor()` method, add the following code to create the form object. - + - In Angular, `FormGroup` and `FormControl` are types that enable you to build forms. The `FormControl` type can provide a default value and shape the form data. In this example `firstName` is a `string` and the default value is empty string. + In Angular, `FormGroup` and `FormControl` are types that enable you to build forms. The `FormControl` type can provide a default value and shape the form data. In this example `firstName` is a `string` and the default value is empty string. -1. In the `Details` class, after the `constructor()` method, add the following code to handle the **Apply now** click. +1. In the `Details` class, after the `constructor()` method, add the following code to handle the **Apply now** click. - + - This button does not exist yet - you will add it in the next step. In the above code, the `FormControl`s may return `null`. This code uses the nullish coalescing operator to default to empty string if the value is `null`. + This button does not exist yet - you will add it in the next step. In the above code, the `FormControl`s may return `null`. This code uses the nullish coalescing operator to default to empty string if the value is `null`. -1. Confirm that the app builds without error. - Correct any errors before you continue to the next step. - +1. Confirm that the app builds without error. + Correct any errors before you continue to the next step. + This step adds the markup to the details page that displays the form. @@ -74,7 +74,7 @@ In the **Edit** pane of your IDE, in `src/app/details/details.ts`: 1. Confirm that the app builds without error. Correct any errors before you continue to the next step. -details page with a form for applying to live at this location + details page with a form for applying to live at this location diff --git a/adev/src/content/tutorials/first-app/steps/12-forms/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/12-forms/src/app/home/home.ts index 43a2b0002cf5..a0303bb1ebbd 100644 --- a/adev/src/content/tutorials/first-app/steps/12-forms/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/12-forms/src/app/home/home.ts @@ -14,7 +14,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of housingLocationList; track $index) { + @for (housingLocation of housingLocationList; track $index) { }
    diff --git a/adev/src/content/tutorials/first-app/steps/12-forms/src/index.html b/adev/src/content/tutorials/first-app/steps/12-forms/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/12-forms/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/12-forms/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/13-search/README.md b/adev/src/content/tutorials/first-app/steps/13-search/README.md index 00d774a54a47..a88b9608eb95 100644 --- a/adev/src/content/tutorials/first-app/steps/13-search/README.md +++ b/adev/src/content/tutorials/first-app/steps/13-search/README.md @@ -53,18 +53,19 @@ The `Home` already contains an input field that you will use to capture input fr The template has been updated to bind the `filterResults` function to the `click` event. Next, your task is to implement the `filterResults` function in the `Home` class. -1. Update the `Home` class to include the implementation of the `filterResults` function. +1. Update the `Home` class to include the implementation of the `filterResults` function. - + - This function uses the `String` `filter` function to compare the value of the `text` parameter against the `housingLocation.city` property. You can update this function to match against any property or multiple properties for a fun exercise. + This function uses the `String` `filter` function to compare the value of the `text` parameter against the `housingLocation.city` property. You can update this function to match against any property or multiple properties for a fun exercise. -1. Save your code. +1. Save your code. -1. Refresh the browser and confirm that you can search the housing location data by city when you click the "Search" button after entering text. +1. Refresh the browser and confirm that you can search the housing location data by city when you click the "Search" button after entering text. -filtered search results based on user input - + filtered search results based on user input + + diff --git a/adev/src/content/tutorials/first-app/steps/13-search/src/app/app.ts b/adev/src/content/tutorials/first-app/steps/13-search/src/app/app.ts index c9be1236e28a..fa549e7c45ae 100644 --- a/adev/src/content/tutorials/first-app/steps/13-search/src/app/app.ts +++ b/adev/src/content/tutorials/first-app/steps/13-search/src/app/app.ts @@ -13,7 +13,7 @@ import {RouterLink, RouterOutlet} from '@angular/router';
    - +
    `, diff --git a/adev/src/content/tutorials/first-app/steps/13-search/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/13-search/src/app/home/home.ts index 43a2b0002cf5..a0303bb1ebbd 100644 --- a/adev/src/content/tutorials/first-app/steps/13-search/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/13-search/src/app/home/home.ts @@ -14,7 +14,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of housingLocationList; track $index) { + @for (housingLocation of housingLocationList; track $index) { }
    diff --git a/adev/src/content/tutorials/first-app/steps/13-search/src/index.html b/adev/src/content/tutorials/first-app/steps/13-search/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/13-search/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/13-search/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/14-http/README.md b/adev/src/content/tutorials/first-app/steps/14-http/README.md index 57d30cf420f2..2515b3e606b2 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/README.md +++ b/adev/src/content/tutorials/first-app/steps/14-http/README.md @@ -138,9 +138,9 @@ JSON Server is an open source tool used to create mock REST APIs. You'll use it 1. Time to test your configuration. From the command line, at the root of your project run the following commands. -```bash -json-server --watch db.json -``` + ```bash + json-server --watch db.json + ``` 1. In your web browser, navigate to the `http://localhost:3000/locations` and confirm that the response includes the data stored in `db.json`. @@ -150,51 +150,51 @@ If you have any trouble with your configuration, you can find more details in th The data source has been configured, the next step is to update your web app to connect to it use the data. -1. In `src/app/housing.service.ts`, make the following changes: +1. In `src/app/housing.service.ts`, make the following changes: -1. Update the code to remove `housingLocationList` property and the array containing the data, as well as the `baseUrl` property. +1. Update the code to remove `housingLocationList` property and the array containing the data, as well as the `baseUrl` property. -1. Add a string property called `url` and set its value to `'http://localhost:3000/locations'` +1. Add a string property called `url` and set its value to `'http://localhost:3000/locations'` - + - This code will result in errors in the rest of the file because it depends on the `housingLocationList` property. We're going to update the service methods next. + This code will result in errors in the rest of the file because it depends on the `housingLocationList` property. We're going to update the service methods next. -1. Update the `getAllHousingLocations` function to make a call to the web server you configured. +1. Update the `getAllHousingLocations` function to make a call to the web server you configured. - + - The code now uses asynchronous code to make a **GET** request over HTTP. + The code now uses asynchronous code to make a **GET** request over HTTP. - HELPFUL: For this example, the code uses `fetch`. For more advanced use cases consider using `HttpClient` provided by Angular. + HELPFUL: For this example, the code uses `fetch`. For more advanced use cases consider using `HttpClient` provided by Angular. -1. Update the `getHousingLocationsById` function to make a call to the web server you configured. +1. Update the `getHousingLocationsById` function to make a call to the web server you configured. - HELPFUL: Notice the `fetch` method has been updated to _query_ the data for location with a matching `id` property value. See [URL Search Parameter](https://developer.mozilla.org/en-US/docs/Web/API/URL/search) for more information. + HELPFUL: Notice the `fetch` method has been updated to _query_ the data for location with a matching `id` property value. See [URL Search Parameter](https://developer.mozilla.org/en-US/docs/Web/API/URL/search) for more information. - + -1. Once all the updates are complete, your updated service should match the following code. +1. Once all the updates are complete, your updated service should match the following code. - + The server is now reading data from the HTTP request but the components that rely on the service now have errors because they were programmed to use the synchronous version of the service. -1. In `src/app/home/home.ts`, update the `constructor` to use the new asynchronous version of the `getAllHousingLocations` method. +1. In `src/app/home/home.ts`, update the `constructor` to use the new asynchronous version of the `getAllHousingLocations` method. Because we didn't use signals for our state, you have to notify Angular that a change happened that requires a synchronization. Call `this.changeDetectorRef.markForCheck()` to do this. - + -1. In `src/app/details/details.ts`, update the `constructor` to use the new asynchronous version of the `getHousingLocationById` method. +1. In `src/app/details/details.ts`, update the `constructor` to use the new asynchronous version of the `getHousingLocationById` method. As before, you must also call `this.changeDetectorRef.markForCheck()` to notify Angular of the changes. - + -1. Save your code. +1. Save your code. -1. Open the application in the browser and confirm that it runs without any errors. - +1. Open the application in the browser and confirm that it runs without any errors. + diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/details/details.ts b/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/details/details.ts index f8ac4306c051..30632c7a3373 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/details/details.ts +++ b/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/details/details.ts @@ -1,4 +1,4 @@ -import {Component, inject} from '@angular/core'; +import {ChangeDetectorRef, Component, inject} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {HousingService} from '../housing.service'; import {HousingLocationInfo} from '../housinglocation'; @@ -46,6 +46,7 @@ import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; styleUrls: ['./details.css'], }) export class Details { + private readonly changeDetectorRef = inject(ChangeDetectorRef); route: ActivatedRoute = inject(ActivatedRoute); housingService = inject(HousingService); housingLocation: HousingLocationInfo | undefined; @@ -60,6 +61,7 @@ export class Details { const housingLocationId = parseInt(this.route.snapshot.params['id'], 10); this.housingService.getHousingLocationById(housingLocationId).then((housingLocation) => { this.housingLocation = housingLocation; + this.changeDetectorRef.markForCheck(); }); } diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/home/home.ts index 572baab90047..38586b25d1bf 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/14-http/src-final/app/home/home.ts @@ -1,4 +1,4 @@ -import {Component, inject} from '@angular/core'; +import {ChangeDetectorRef, Component, inject} from '@angular/core'; import {HousingLocation} from '../housing-location/housing-location'; import {HousingLocationInfo} from '../housinglocation'; import {HousingService} from '../housing.service'; @@ -14,7 +14,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of filteredLocationList; track $index) { + @for (housingLocation of filteredLocationList; track $index) { }
    @@ -22,6 +22,7 @@ import {HousingService} from '../housing.service'; styleUrls: ['./home.css'], }) export class Home { + private readonly changeDetectorRef = inject(ChangeDetectorRef); housingLocationList: HousingLocationInfo[] = []; housingService: HousingService = inject(HousingService); filteredLocationList: HousingLocationInfo[] = []; @@ -32,6 +33,7 @@ export class Home { .then((housingLocationList: HousingLocationInfo[]) => { this.housingLocationList = housingLocationList; this.filteredLocationList = housingLocationList; + this.changeDetectorRef.markForCheck(); }); } diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src-final/index.html b/adev/src/content/tutorials/first-app/steps/14-http/src-final/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src-final/index.html +++ b/adev/src/content/tutorials/first-app/steps/14-http/src-final/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src/app/details/details.ts b/adev/src/content/tutorials/first-app/steps/14-http/src/app/details/details.ts index 6a06dc99d586..7f9980b483d8 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src/app/details/details.ts +++ b/adev/src/content/tutorials/first-app/steps/14-http/src/app/details/details.ts @@ -1,4 +1,4 @@ -import {Component, inject} from '@angular/core'; +import {ChangeDetectorRef, Component, inject} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {HousingService} from '../housing.service'; import {HousingLocationInfo} from '../housinglocation'; @@ -46,6 +46,7 @@ import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; styleUrls: ['./details.css'], }) export class Details { + private readonly changeDetectorRef = inject(ChangeDetectorRef); route: ActivatedRoute = inject(ActivatedRoute); housingService = inject(HousingService); housingLocation: HousingLocationInfo | undefined; diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src/app/home/home.ts b/adev/src/content/tutorials/first-app/steps/14-http/src/app/home/home.ts index 0680f17f43f9..a90c4bd5ac83 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src/app/home/home.ts +++ b/adev/src/content/tutorials/first-app/steps/14-http/src/app/home/home.ts @@ -1,4 +1,4 @@ -import {Component, inject} from '@angular/core'; +import {ChangeDetectorRef, Component, inject} from '@angular/core'; import {HousingLocation} from '../housing-location/housing-location'; import {HousingLocationInfo} from '../housinglocation'; import {HousingService} from '../housing.service'; @@ -14,7 +14,7 @@ import {HousingService} from '../housing.service';
    - @for(housingLocation of filteredLocationList; track $index) { + @for (housingLocation of filteredLocationList; track $index) { }
    @@ -22,6 +22,7 @@ import {HousingService} from '../housing.service'; styleUrls: ['./home.css'], }) export class Home { + private readonly changeDetectorRef = inject(ChangeDetectorRef); housingLocationList: HousingLocationInfo[] = []; housingService: HousingService = inject(HousingService); filteredLocationList: HousingLocationInfo[] = []; diff --git a/adev/src/content/tutorials/first-app/steps/14-http/src/index.html b/adev/src/content/tutorials/first-app/steps/14-http/src/index.html index b8658772264e..202fb0b3c23a 100644 --- a/adev/src/content/tutorials/first-app/steps/14-http/src/index.html +++ b/adev/src/content/tutorials/first-app/steps/14-http/src/index.html @@ -1,14 +1,17 @@ - + - - - Homes - - - - - - - - + + + Homes + + + + + + + + diff --git a/adev/src/content/tutorials/homepage/package-lock.json b/adev/src/content/tutorials/homepage/package-lock.json index f158d7214b3b..a2472ec4d85b 100644 --- a/adev/src/content/tutorials/homepage/package-lock.json +++ b/adev/src/content/tutorials/homepage/package-lock.json @@ -2344,15 +2344,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -2374,11 +2374,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -2427,25 +2427,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2508,16 +2498,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2595,16 +2575,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3584,6 +3554,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3843,9 +3823,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4003,11 +3983,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4475,9 +4455,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4706,19 +4686,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4821,9 +4802,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4835,7 +4816,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5053,11 +5038,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5635,16 +5620,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6065,16 +6040,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6166,16 +6131,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6190,16 +6145,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6236,16 +6181,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6398,16 +6333,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6520,11 +6445,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6620,13 +6545,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/learn-angular/common/package-lock.json b/adev/src/content/tutorials/learn-angular/common/package-lock.json index 48137ad6f816..daa68c844149 100644 --- a/adev/src/content/tutorials/learn-angular/common/package-lock.json +++ b/adev/src/content/tutorials/learn-angular/common/package-lock.json @@ -2363,15 +2363,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -2393,11 +2393,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -2446,25 +2446,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2527,16 +2517,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2614,16 +2594,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3603,6 +3573,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3862,9 +3842,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4022,11 +4002,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4494,9 +4474,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4725,19 +4705,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4840,9 +4821,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4854,7 +4835,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5072,11 +5057,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5654,16 +5639,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6084,16 +6059,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6185,16 +6150,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6209,16 +6164,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6255,16 +6200,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6417,16 +6352,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6539,11 +6464,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6639,13 +6564,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/learn-angular/intro/src/app/app.ts b/adev/src/content/tutorials/learn-angular/intro/src/app/app.ts index 6b43cd5dc499..7b25d54b5e53 100644 --- a/adev/src/content/tutorials/learn-angular/intro/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/intro/src/app/app.ts @@ -2,8 +2,6 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` - Welcome to Angular! - `, + template: ` Welcome to Angular! `, }) export class App {} diff --git a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/README.md b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/README.md index f67cf24b7928..52178220a278 100644 --- a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/README.md +++ b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/README.md @@ -6,7 +6,7 @@ Components are the foundational building blocks for any Angular application. Eac - HTML template - CSS styles -Note: Learn more about [components in the essentials guide](/essentials/components). +NOTE: Learn more about [components in the essentials guide](/essentials/components). In this activity, you'll learn how to update the template and styles of a component. diff --git a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/answer/src/app/app.ts b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/answer/src/app/app.ts index cb52ccb56808..80434433d264 100644 --- a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/answer/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/answer/src/app/app.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` - Hello Universe - `, + template: ` Hello Universe `, styles: ` :host { color: #a144eb; diff --git a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/src/app/app.ts b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/src/app/app.ts index 0a27c4c16d7a..60faaa1bb659 100644 --- a/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/steps/1-components-in-angular/src/app/app.ts @@ -2,9 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'app-root', - template: ` - Hello - `, + template: ` Hello `, styles: ` :host { color: blue; diff --git a/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/README.md b/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/README.md index fc0a174101a4..3e7f5bf8ab7a 100644 --- a/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/README.md +++ b/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/README.md @@ -4,7 +4,7 @@ Sometimes in app development, you end up with a lot of components that you need Maybe they are below the visible fold or are heavy components that aren't interacted with until later. In that case, we can load some of those resources later with deferrable views. -Note: Learn more about [deferred loading with @defer in the in-depth guide](/guide/templates/defer). +NOTE: Learn more about [deferred loading with @defer in the in-depth guide](/guide/templates/defer). In this activity, you'll learn how to use deferrable views to defer load a section of your component template. @@ -32,13 +32,13 @@ The code above is an example of how to use a basic `@defer` block. By default `@ Add a `@placeholder` block to the `@defer` block. The `@placeholder` block is where you put html that will show before the deferred loading starts. The content in `@placeholder` blocks is eagerly loaded. - +```angular-html {highlight:[3,4,5]} @defer { } @placeholder {

    Future comments

    } -
    +``` @@ -46,7 +46,7 @@ Add a `@placeholder` block to the `@defer` block. The `@placeholder` block is wh Add a `@loading` block to the `@defer` block. The `@loading` block is where you put html that will show _while_ the deferred content is actively being fetched, but hasn't finished yet. The content in `@loading` blocks is eagerly loaded. - +```angular-html {highlight:[5,6,7]} @defer { } @placeholder { @@ -54,7 +54,7 @@ Add a `@loading` block to the `@defer` block. The `@loading` block is where you } @loading {

    Loading comments...

    } -
    +``` @@ -62,7 +62,7 @@ Add a `@loading` block to the `@defer` block. The `@loading` block is where you Both `@placeholder` and `@loading` sections have optional parameters to prevent flickering from occurring when loading happens quickly. `@placeholder` has `minimum` and `@loading` has `minimum` and `after`. Add a `minimum` duration to the `@loading` block so it will be rendered for at least 2 seconds. - +```angular-html {highlight:[5]} @defer { } @placeholder { @@ -70,7 +70,7 @@ Both `@placeholder` and `@loading` sections have optional parameters to prevent } @loading (minimum 2s) {

    Loading comments...

    } -
    +``` @@ -78,11 +78,11 @@ Both `@placeholder` and `@loading` sections have optional parameters to prevent Deferrable views have a number of trigger options. Add a viewport trigger so the content will defer load once it enters the viewport. - +```angular-html {highlight:[1]} @defer (on viewport) { } - +``` @@ -90,16 +90,49 @@ Deferrable views have a number of trigger options. Add a viewport trigger so the A viewport trigger is best used when you're deferring content that's far enough down the page that it needs to be scrolled to see. So let's add some content to our blog post. You can either write your own, or you can copy the content below and put it inside the `
    ` element. - +```html {highlight:[1]}
    -

    Angular is my favorite framework, and this is why. Angular has the coolest deferrable view feature that makes defer loading content the easiest and most ergonomic it could possibly be. The Angular community is also filled with amazing contributors and experts that create excellent content. The community is welcoming and friendly, and it really is the best community out there.

    -

    I can't express enough how much I enjoy working with Angular. It offers the best developer experience I've ever had. I love that the Angular team puts their developers first and takes care to make us very happy. They genuinely want Angular to be the best framework it can be, and they're doing such an amazing job at it, too. This statement comes from my heart and is not at all copied and pasted. In fact, I think I'll say these exact same things again a few times.

    -

    Angular is my favorite framework, and this is why. Angular has the coolest deferrable view feature that makes defer loading content the easiest and most ergonomic it could possibly be. The Angular community is also filled with amazing contributors and experts that create excellent content. The community is welcoming and friendly, and it really is the best community out there.

    -

    I can't express enough how much I enjoy working with Angular. It offers the best developer experience I've ever had. I love that the Angular team puts their developers first and takes care to make us very happy. They genuinely want Angular to be the best framework it can be, and they're doing such an amazing job at it, too. This statement comes from my heart and is not at all copied and pasted. In fact, I think I'll say these exact same things again a few times.

    -

    Angular is my favorite framework, and this is why. Angular has the coolest deferrable view feature that makes defer loading content the easiest and most ergonomic it could possibly be. The Angular community is also filled with amazing contributors and experts that create excellent content. The community is welcoming and friendly, and it really is the best community out there.

    -

    I can't express enough how much I enjoy working with Angular. It offers the best developer experience I've ever had. I love that the Angular team puts their developers first and takes care to make us very happy. They genuinely want Angular to be the best framework it can be, and they're doing such an amazing job at it, too. This statement comes from my heart and is not at all copied and pasted.

    +

    + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly be. + The Angular community is also filled with amazing contributors and experts that create excellent + content. The community is welcoming and friendly, and it really is the best community out there. +

    +

    + I can't express enough how much I enjoy working with Angular. It offers the best developer + experience I've ever had. I love that the Angular team puts their developers first and takes + care to make us very happy. They genuinely want Angular to be the best framework it can be, and + they're doing such an amazing job at it, too. This statement comes from my heart and is not at + all copied and pasted. In fact, I think I'll say these exact same things again a few times. +

    +

    + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly be. + The Angular community is also filled with amazing contributors and experts that create excellent + content. The community is welcoming and friendly, and it really is the best community out there. +

    +

    + I can't express enough how much I enjoy working with Angular. It offers the best developer + experience I've ever had. I love that the Angular team puts their developers first and takes + care to make us very happy. They genuinely want Angular to be the best framework it can be, and + they're doing such an amazing job at it, too. This statement comes from my heart and is not at + all copied and pasted. In fact, I think I'll say these exact same things again a few times. +

    +

    + Angular is my favorite framework, and this is why. Angular has the coolest deferrable view + feature that makes defer loading content the easiest and most ergonomic it could possibly be. + The Angular community is also filled with amazing contributors and experts that create excellent + content. The community is welcoming and friendly, and it really is the best community out there. +

    +

    + I can't express enough how much I enjoy working with Angular. It offers the best developer + experience I've ever had. I love that the Angular team puts their developers first and takes + care to make us very happy. They genuinely want Angular to be the best framework it can be, and + they're doing such an amazing job at it, too. This statement comes from my heart and is not at + all copied and pasted. +

    -
    +``` Once you've added this code, now scroll down to see the deferred content load once you scroll it into the viewport. diff --git a/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/answer/src/app/app.ts b/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/answer/src/app/app.ts index 787f0f5c673c..198ad2d13ed0 100644 --- a/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/answer/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/steps/10-deferrable-views/answer/src/app/app.ts @@ -54,11 +54,11 @@ import {Comments} from './comments';
    @defer (on viewport) { - + } @placeholder { -

    Future comments

    +

    Future comments

    } @loading (minimum 2s) { -

    Loading comments...

    +

    Loading comments...

    }
    `, diff --git a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/README.md b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/README.md index 6c085fe927ca..7e683d7f5b34 100644 --- a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/README.md +++ b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/README.md @@ -4,7 +4,7 @@ Images are a big part of many applications, and can be a major contributor to ap Image optimization can be a complex topic, but Angular handles most of it for you, with the `NgOptimizedImage` directive. -Note: Learn more about [image optimization with NgOptimizedImage in the in-depth guide](/guide/image-optimization). +NOTE: Learn more about [image optimization with NgOptimizedImage in the in-depth guide](/guide/image-optimization). In this activity, you'll learn how to use `NgOptimizedImage` to ensure your images are loaded efficiently. @@ -31,7 +31,7 @@ import { NgOptimizedImage } from '@angular/common'; To enable the `NgOptimizedImage` directive, swap out the `src` attribute for `ngSrc`. This applies for both static image sources (i.e., `src`) and dynamic image sources (i.e., `[src]`). - +```angular-ts {highlight:[[7],[11]]} import { NgOptimizedImage } from '@angular/common'; @Component({ @@ -48,7 +48,7 @@ template: ` ... `, imports: [NgOptimizedImage], }) - +``` @@ -83,9 +83,7 @@ One of the most important optimizations for loading performance is to prioritize `NgOptimizedImage` allows you to specify an [image loader](guide/image-optimization#configuring-an-image-loader-for-ngoptimizedimage), which tells the directive how to format URLs for your images. Using a loader allows you to define your images with short, relative URLs: ```ts -providers: [ - provideImgixLoader('https://my.base.url/'), -] +providers: [provideImgixLoader('https://my.base.url/')]; ``` Final URL will be 'https://my.base.url/image.png' diff --git a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/answer/src/app/app.ts b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/answer/src/app/app.ts index 6ad5866234c2..65b5095fff04 100644 --- a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/answer/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/answer/src/app/app.ts @@ -3,9 +3,7 @@ import {User} from './user'; @Component({ selector: 'app-root', - template: ` - - `, + template: ` `, imports: [User], }) export class App {} diff --git a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/src/app/app.ts b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/src/app/app.ts index 6ad5866234c2..65b5095fff04 100644 --- a/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/src/app/app.ts +++ b/adev/src/content/tutorials/learn-angular/steps/11-optimizing-images/src/app/app.ts @@ -3,9 +3,7 @@ import {User} from './user'; @Component({ selector: 'app-root', - template: ` - - `, + template: ` `, imports: [User], }) export class App {} diff --git a/adev/src/content/tutorials/learn-angular/steps/12-enable-routing/README.md b/adev/src/content/tutorials/learn-angular/steps/12-enable-routing/README.md index 3009baa3143d..c95112fbf615 100644 --- a/adev/src/content/tutorials/learn-angular/steps/12-enable-routing/README.md +++ b/adev/src/content/tutorials/learn-angular/steps/12-enable-routing/README.md @@ -2,7 +2,7 @@ For most apps, there comes a point where the app requires more than a single page. When that time inevitably comes, routing becomes a big part of the performance story for users. -Note: Learn more about [routing in the in-depth guide](/guide/routing). +NOTE: Learn more about [routing in the in-depth guide](/guide/routing). In this activity, you'll learn how to set up and configure your app to use Angular Router. @@ -33,15 +33,15 @@ In `app.config.ts`, configure the app to Angular Router with the following steps 1. Import `routes` from the `./app.routes.ts`. 1. Call the `provideRouter` function with `routes` passed in as an argument in the `providers` array. - +```ts {highlight:[2,3,6]} import {ApplicationConfig} from '@angular/core'; import {provideRouter} from '@angular/router'; import {routes} from './app.routes'; export const appConfig: ApplicationConfig = { -providers: [provideRouter(routes)], + providers: [provideRouter(routes)], }; - +``` @@ -51,12 +51,13 @@ Finally, to make sure your app is ready to use the Angular Router, you need to t Update the template for `App` by adding `` - +```angular-ts {highlight:[11]} import {RouterOutlet} from '@angular/router'; @Component({ ... -template: `

    viewBox="0 0 103.41 84.33" [class.show]="rotateVal() >= 74" > - @switch(updatedInteractions().face) { @case(0) { - - - - - - - - } @case(1) { - - - - - - - - } @case(2) { - - - - - - - - } @case(3) { - - - - - - } @case(4) { - - - - - - - } @default { - - - - - - - - } } + @switch (updatedInteractions().face) { + @case (0) { + + + + + + + + } + @case (1) { + + + + + + + + } + @case (2) { + + + + + + + + } + @case (3) { + + + + + + } + @case (4) { + + + + + + + } + @default { + + + + + + + + } + }
    @@ -127,8 +143,10 @@

    Goal: {{ goal() }}º

    level: {{ gameStats().level + 1 }}

    - accuracy: {{ totalAccuracyPercentage() > 0 ? (totalAccuracyPercentage() | number : '1.1-1') - + '%' : '??' }} + accuracy: + {{ + totalAccuracyPercentage() > 0 ? (totalAccuracyPercentage() | number: '1.1-1') + '%' : '??' + }}

    - @if(isGuessModalOpen()) { - - -
    -

    goal: {{ goal() }}º

    -

    actual: {{ rotateVal() | number : '1.1-1' }}º

    -
    -

    - {{ animatedAccuracy() | number : '1.1-1' }}% - accurate -

    - - - - - - - - - @if(animatedAccuracy() > 95) { - - - - - - - - } @else if (animatedAccuracy() > 80) { - - - - - - } @else if (animatedAccuracy() > 60) { - - - - - - - - } @else if (animatedAccuracy() > 40) { - - - - - - - - } @else { - - - - - - - - } - -
    "{{ resultQuote() }}"
    -
    - - share -
    -
    + @if (isGuessModalOpen()) { + + +
    +

    goal: {{ goal() }}º

    +

    actual: {{ rotateVal() | number: '1.1-1' }}º

    +
    +

    + {{ animatedAccuracy() | number: '1.1-1' }}% + accurate +

    + + + + + + + + + @if (animatedAccuracy() > 95) { + + + + + + + + } @else if (animatedAccuracy() > 80) { + + + + + + } @else if (animatedAccuracy() > 60) { + + + + + + + + } @else if (animatedAccuracy() > 40) { + + + + + + + + } @else { + + + + + + + + } + +
    "{{ resultQuote() }}"
    +
    + + share +
    +
    }
    - @if(isAccessiblePanelOpen()) { -
    - - - - -
    + @if (isAccessiblePanelOpen()) { +
    + + + + +
    } =22" } @@ -2477,25 +2477,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2558,16 +2548,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2645,16 +2625,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3634,6 +3604,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3893,9 +3873,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4053,11 +4033,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4525,9 +4505,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4756,19 +4736,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4871,9 +4852,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4885,7 +4866,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5103,11 +5088,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5685,16 +5670,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6115,16 +6090,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6216,16 +6181,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6240,16 +6195,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6286,16 +6231,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6448,16 +6383,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6568,11 +6493,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6668,13 +6593,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/signal-forms/common/package-lock.json b/adev/src/content/tutorials/signal-forms/common/package-lock.json index d8a6f26863f7..3d71b53de30a 100644 --- a/adev/src/content/tutorials/signal-forms/common/package-lock.json +++ b/adev/src/content/tutorials/signal-forms/common/package-lock.json @@ -2363,15 +2363,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -2393,11 +2393,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -2446,25 +2446,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2527,16 +2517,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2614,16 +2594,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3603,6 +3573,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3862,9 +3842,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4022,11 +4002,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4494,9 +4474,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4725,19 +4705,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4840,9 +4821,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4854,7 +4835,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5072,11 +5057,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5654,16 +5639,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6084,16 +6059,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6185,16 +6150,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6209,16 +6164,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6255,16 +6200,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6417,16 +6352,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6539,11 +6464,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6639,13 +6564,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/signal-forms/steps/1-set-up-form-model/README.md b/adev/src/content/tutorials/signal-forms/steps/1-set-up-form-model/README.md index 68988c328f58..ad37428dab6a 100644 --- a/adev/src/content/tutorials/signal-forms/steps/1-set-up-form-model/README.md +++ b/adev/src/content/tutorials/signal-forms/steps/1-set-up-form-model/README.md @@ -36,8 +36,8 @@ Add this interface above the `@Component` decorator. Import the `signal` function from `@angular/core` and the `form` function from `@angular/forms/signals`: ```ts -import { Component, signal } from '@angular/core'; -import { form } from '@angular/forms/signals'; +import {Component, signal} from '@angular/core'; +import {form} from '@angular/forms/signals'; ``` diff --git a/adev/src/content/tutorials/signal-forms/steps/2-connect-form-template/README.md b/adev/src/content/tutorials/signal-forms/steps/2-connect-form-template/README.md index 81910530f9ea..de371dc11e86 100644 --- a/adev/src/content/tutorials/signal-forms/steps/2-connect-form-template/README.md +++ b/adev/src/content/tutorials/signal-forms/steps/2-connect-form-template/README.md @@ -63,7 +63,7 @@ Add the `[field]` directive to the checkbox input: Below the form, there's a debug section to show current form values. Display each field value using `.value()`: -```html +```angular-html

    Email: {{ loginForm.email().value() }}

    Password: {{ loginForm.password().value() ? '••••••••' : '(empty)' }}

    Remember me: {{ loginForm.rememberMe().value() ? 'Yes' : 'No' }}

    diff --git a/adev/src/content/tutorials/signal-forms/steps/3-add-validation/README.md b/adev/src/content/tutorials/signal-forms/steps/3-add-validation/README.md index bc5f431c800c..c90742750006 100644 --- a/adev/src/content/tutorials/signal-forms/steps/3-add-validation/README.md +++ b/adev/src/content/tutorials/signal-forms/steps/3-add-validation/README.md @@ -18,7 +18,7 @@ Let's add validation! Import the `required` and `email` validators from `@angular/forms/signals`: ```ts -import { form, Field, required, email } from '@angular/forms/signals'; +import {form, Field, required, email} from '@angular/forms/signals'; ```
    @@ -39,8 +39,8 @@ Inside the schema function, add validation for the email field. Use both `requir ```ts loginForm = form(this.loginModel, (fieldPath) => { - required(fieldPath.email, { message: 'Email is required' }); - email(fieldPath.email, { message: 'Enter a valid email address' }); + required(fieldPath.email, {message: 'Email is required'}); + email(fieldPath.email, {message: 'Enter a valid email address'}); }); ``` @@ -52,9 +52,9 @@ Add validation for the password field using the `required()` validator: ```ts loginForm = form(this.loginModel, (fieldPath) => { - required(fieldPath.email, { message: 'Email is required' }); - email(fieldPath.email, { message: 'Enter a valid email address' }); - required(fieldPath.password, { message: 'Password is required' }); + required(fieldPath.email, {message: 'Email is required'}); + email(fieldPath.email, {message: 'Enter a valid email address'}); + required(fieldPath.password, {message: 'Password is required'}); }); ``` diff --git a/adev/src/content/tutorials/signal-forms/steps/4-display-errors/README.md b/adev/src/content/tutorials/signal-forms/steps/4-display-errors/README.md index 9b278dfb4cde..39facf6eee66 100644 --- a/adev/src/content/tutorials/signal-forms/steps/4-display-errors/README.md +++ b/adev/src/content/tutorials/signal-forms/steps/4-display-errors/README.md @@ -18,7 +18,7 @@ Let's display validation feedback! Below the email input, add conditional error display. This will only show errors when the field is both invalid and touched: -```html +```angular-html @@ -54,9 +54,7 @@ In your template, bind the `onSubmit()` method to the form's submit event: Update the submit button to be disabled when the form is invalid: ```html - + ``` This prevents submission when the form has validation errors. diff --git a/adev/src/content/tutorials/signal-forms/steps/5-add-submission/answer/src/app/app.html b/adev/src/content/tutorials/signal-forms/steps/5-add-submission/answer/src/app/app.html index 3d1dc66025a9..441032c934a0 100644 --- a/adev/src/content/tutorials/signal-forms/steps/5-add-submission/answer/src/app/app.html +++ b/adev/src/content/tutorials/signal-forms/steps/5-add-submission/answer/src/app/app.html @@ -5,11 +5,11 @@ @if (loginForm.email().invalid() && loginForm.email().touched()) { -
    - @for (error of loginForm.email().errors(); track error.kind) { - {{ error.message }} - } -
    +
    + @for (error of loginForm.email().errors(); track error.kind) { + {{ error.message }} + } +
    }
    @@ -19,11 +19,11 @@ @if (loginForm.password().invalid() && loginForm.password().touched()) { -
    - @for (error of loginForm.password().errors(); track error.kind) { - {{ error.message }} - } -
    +
    + @for (error of loginForm.password().errors(); track error.kind) { + {{ error.message }} + } +
    }
    diff --git a/adev/src/content/tutorials/signal-forms/steps/5-add-submission/src/app/app.html b/adev/src/content/tutorials/signal-forms/steps/5-add-submission/src/app/app.html index 5eeee9240403..9025d7cf1d91 100644 --- a/adev/src/content/tutorials/signal-forms/steps/5-add-submission/src/app/app.html +++ b/adev/src/content/tutorials/signal-forms/steps/5-add-submission/src/app/app.html @@ -6,11 +6,11 @@ @if (loginForm.email().invalid() && loginForm.email().touched()) { -
    - @for (error of loginForm.email().errors(); track error.kind) { - {{ error.message }} - } -
    +
    + @for (error of loginForm.email().errors(); track error.kind) { + {{ error.message }} + } +
    }
    @@ -20,11 +20,11 @@ @if (loginForm.password().invalid() && loginForm.password().touched()) { -
    - @for (error of loginForm.password().errors(); track error.kind) { - {{ error.message }} - } -
    +
    + @for (error of loginForm.password().errors(); track error.kind) { + {{ error.message }} + } +
    }
    diff --git a/adev/src/content/tutorials/signals/common/package-lock.json b/adev/src/content/tutorials/signals/common/package-lock.json index d8a6f26863f7..3d71b53de30a 100644 --- a/adev/src/content/tutorials/signals/common/package-lock.json +++ b/adev/src/content/tutorials/signals/common/package-lock.json @@ -2363,15 +2363,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -2393,11 +2393,11 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -2446,25 +2446,15 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -2527,16 +2517,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -2614,16 +2594,6 @@ "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -3603,6 +3573,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@sigstore/tuf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", @@ -3862,9 +3842,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4022,11 +4002,11 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -4494,9 +4474,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.260", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", - "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -4725,19 +4705,20 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -4840,9 +4821,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -4854,7 +4835,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -5072,11 +5057,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5654,16 +5639,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6084,16 +6059,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", @@ -6185,16 +6150,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-packlist": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", @@ -6209,16 +6164,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/npm-pick-manifest": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", @@ -6255,16 +6200,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", - "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6417,16 +6352,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -6539,11 +6464,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -6639,13 +6564,13 @@ "license": "MIT" }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { diff --git a/adev/src/content/tutorials/signals/intro/src/app/app.html b/adev/src/content/tutorials/signals/intro/src/app/app.html index 129f8c9a7e6c..03de4634e557 100644 --- a/adev/src/content/tutorials/signals/intro/src/app/app.html +++ b/adev/src/content/tutorials/signals/intro/src/app/app.html @@ -2,7 +2,11 @@

    Welcome to Angular's Signal Tutorial!

    {{ count() }}

    - @if (isEven()) { Even } @else { Odd } + @if (isEven()) { + Even + } @else { + Odd + }
    diff --git a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md index cd2cf9372d86..00b9a1523373 100644 --- a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md +++ b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md @@ -42,7 +42,7 @@ Update the status indicator to display the current user status by: 1. Binding the signal to the class attribute with `[class]="userStatus()"` 2. Displaying the status text by replacing `???` with `{{ userStatus() }}` -```html +```angular-html
    @@ -83,12 +83,8 @@ The buttons are already in the template. Now connect them to your methods by add ```html - - + + ``` @@ -110,9 +106,7 @@ The `update()` method takes a function that receives the current value and retur The toggle button is already in the template. Connect it to your `toggleStatus()` method: ```html - + ``` diff --git a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/answer/src/app/app.ts index 02d126714661..f16eea2ee023 100644 --- a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/answer/src/app/app.ts @@ -9,17 +9,11 @@ import {Component, signal, ChangeDetectionStrategy} from '@angular/core'; Status: {{ userStatus() }}
    - +
    - - - + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/src/app/app.ts b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/src/app/app.ts index c043693e0dad..86cf6983e1f0 100644 --- a/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/1-creating-your-first-signal/src/app/app.ts @@ -14,15 +14,9 @@ import {Component, ChangeDetectionStrategy} from '@angular/core';
    - - - + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/answer/src/app/app.ts index 87580f36f65f..ea557873aeaf 100644 --- a/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/answer/src/app/app.ts @@ -5,28 +5,30 @@ import {Component, signal, computed, effect, ChangeDetectionStrategy} from '@ang template: `

    Theme Manager with Effects

    - +
    - + @if (!isLoggedIn()) { } @else { }
    - +

    Current theme: {{ theme() }}

    User: {{ username() }}

    -

    Status: +

    + Status: @if (isLoggedIn()) { Logged in } @else { @@ -34,7 +36,7 @@ import {Component, signal, computed, effect, ChangeDetectionStrategy} from '@ang }

    - +

    Open the browser console to see the effects in action!

    Effects are automatically:

    diff --git a/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/src/app/app.ts b/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/src/app/app.ts index 4f3a1f6c2740..d00b073a99fb 100644 --- a/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/src/app/app.ts @@ -14,7 +14,8 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor Dark } @else { Light - } Theme + } + Theme @if (!isLoggedIn()) { @@ -27,7 +28,8 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor

    Current theme: {{ theme() }}

    User: {{ username() }}

    -

    Status: +

    + Status: @if (isLoggedIn()) { Logged in } @else { diff --git a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md index 8305008d1e51..82b786717a02 100644 --- a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md +++ b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md @@ -37,10 +37,14 @@ Add a computed signal that creates a descriptive message based on the user statu statusMessage = computed(() => { const status = this.userStatus(); switch (status) { - case 'online': return 'Available for meetings and messages'; - case 'away': return 'Temporarily away, will respond soon'; - case 'offline': return 'Not available, check back later'; - default: return 'Status unknown'; + case 'online': + return 'Available for meetings and messages'; + case 'away': + return 'Temporarily away, will respond soon'; + case 'offline': + return 'Not available, check back later'; + default: + return 'Status unknown'; } }); ``` @@ -66,31 +70,31 @@ This demonstrates how computed signals can perform calculations and combine mult The template already has placeholders showing "Loading...". Replace them with your computed signals: -1. For notifications, replace `Loading...` with an @if block: +1. For notifications, replace `Loading...` with an `@if` block: -```angular-html -@if (notificationsEnabled()) { - Enabled -} @else { - Disabled -} -``` + ```angular-html + @if (notificationsEnabled()) { + Enabled + } @else { + Disabled + } + ``` -2. For the message, replace `Loading...` with: +1. For the message, replace `Loading...` with: -```angular-html -{{ statusMessage() }} -``` + ```angular-html + {{ statusMessage() }} + ``` -3. For working hours, replace `Loading...` with an @if block: +1. For working hours, replace `Loading...` with an `@if` block: -```angular-html -@if (isWithinWorkingHours()) { - Yes -} @else { - No -} -``` + ```angular-html + @if (isWithinWorkingHours()) { + Yes + } @else { + No + } + ``` Notice how computed signals are called just like regular signals - with parentheses! diff --git a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/answer/src/app/app.ts index 03f340815af5..0c90d97a5445 100644 --- a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/answer/src/app/app.ts @@ -9,21 +9,19 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor Status: {{ userStatus() }}

    - +
    - Notifications: + Notifications: @if (notificationsEnabled()) { Enabled } @else { Disabled }
    -
    - Message: {{ statusMessage() }} -
    +
    Message: {{ statusMessage() }}
    - Within Working Hours: + Within Working Hours: @if (isWithinWorkingHours()) { Yes } @else { @@ -31,20 +29,12 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor }
    - +
    - - - - + + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/src/app/app.ts b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/src/app/app.ts index 946058489c2e..cde33628eb9b 100644 --- a/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/src/app/app.ts @@ -30,18 +30,10 @@ import {Component, signal, ChangeDetectionStrategy} from '@angular/core';
    - - - - + + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/answer/src/app/app.ts index 979f99488293..389f8f9cc746 100644 --- a/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/answer/src/app/app.ts @@ -12,7 +12,7 @@ import {Component, signal, computed, linkedSignal, ChangeDetectionStrategy} from
    - Notifications: + Notifications: @if (notificationsEnabled()) { Enabled } @else { @@ -26,11 +26,9 @@ import {Component, signal, computed, linkedSignal, ChangeDetectionStrategy} from }
    -
    - Message: {{ statusMessage() }} -
    +
    Message: {{ statusMessage() }}
    - Within Working Hours: + Within Working Hours: @if (isWithinWorkingHours()) { Yes } @else { @@ -40,18 +38,10 @@ import {Component, signal, computed, linkedSignal, ChangeDetectionStrategy} from
    - - - - + + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/src/app/app.ts b/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/src/app/app.ts index a86301c86df4..23031f49c9ce 100644 --- a/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/src/app/app.ts @@ -11,10 +11,10 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor Status: {{ userStatus() }} - +
    - Notifications: + Notifications: @if (notificationsEnabled()) { Enabled } @else { @@ -22,11 +22,9 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor }
    -
    - Message: {{ statusMessage() }} -
    +
    Message: {{ statusMessage() }}
    - Within Working Hours: + Within Working Hours: @if (isWithinWorkingHours()) { Yes } @else { @@ -34,20 +32,12 @@ import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/cor }
    - +
    - - - - + + + +
    `, diff --git a/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md b/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md index da3a520b60e9..9c78d2f94139 100644 --- a/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md +++ b/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md @@ -27,8 +27,8 @@ Add a property in the component class that creates a resource to load user data userId = signal(1); userResource = resource({ - params: () => ({ id: this.userId() }), - loader: (params) => loadUser(params.params.id) + params: () => ({id: this.userId()}), + loader: (params) => loadUser(params.params.id), }); ``` diff --git a/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/answer/src/app/app.ts index c0c22be08609..73893614619e 100644 --- a/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/answer/src/app/app.ts @@ -6,14 +6,14 @@ import {loadUser} from './user-api'; template: `

    User Profile Loader

    - +
    - +
    @if (isLoading()) {

    Loading user...

    diff --git a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md index 636cac4f2216..e532f9e78009 100644 --- a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md +++ b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md @@ -49,18 +49,10 @@ Update the `product-card` usage in `app.ts` to pass dynamic signal values instea ```html - + - + ``` The square brackets `[]` create property bindings that pass the current signal values to the child. diff --git a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/answer/src/app/product-card.ts b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/answer/src/app/product-card.ts index 1e116f5066cb..c678c169c483 100644 --- a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/answer/src/app/product-card.ts +++ b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/answer/src/app/product-card.ts @@ -7,7 +7,7 @@ import {Component, input, ChangeDetectionStrategy} from '@angular/core';

    {{ name() }}

    \${{ price() }}

    - Status: + Status: @if (available()) { Available } @else { diff --git a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/src/app/app.ts b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/src/app/app.ts index 866843593e1e..4073c92a0867 100644 --- a/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/5-component-communication-with-signals/src/app/app.ts @@ -14,11 +14,7 @@ import {ProductCard} from './product-card';

    Data flows down from parent to child via signal inputs:

    - +
    diff --git a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md index f451bc0ae88f..6050acb661a1 100644 --- a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md +++ b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md @@ -30,10 +30,7 @@ Build the checkbox template that responds to clicks and updates its own model. ```html diff --git a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/app.ts index 8b2a8cbe2541..5771c1ac5665 100644 --- a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/app.ts @@ -8,29 +8,24 @@ import {CustomCheckbox} from './custom-checkbox';

    Custom Checkbox Example

    -
    - + - +
    -

    Terms agreed: +

    + Terms agreed: @if (agreedToTerms()) { Yes } @else { No }

    -

    Notifications: +

    + Notifications: @if (enableNotifications()) { Enabled } @else { diff --git a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/custom-checkbox.ts b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/custom-checkbox.ts index 3203c87854b0..75b3129f61fc 100644 --- a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/custom-checkbox.ts +++ b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/answer/src/app/custom-checkbox.ts @@ -4,10 +4,7 @@ import {Component, model, input, ChangeDetectionStrategy} from '@angular/core'; selector: 'custom-checkbox', template: ` diff --git a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/src/app/app.ts b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/src/app/app.ts index fe94cdbf7344..3568966f45d8 100644 --- a/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/src/app/app.ts @@ -24,11 +24,13 @@ import {CustomCheckbox} from './custom-checkbox'; -->

    -

    Terms agreed: +

    + Terms agreed: ???

    -

    Notifications: +

    + Notifications: ???

    diff --git a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md index 15cc2268338f..7dcb21eebaa8 100644 --- a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md +++ b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md @@ -78,7 +78,7 @@ import {CartDisplay} from './cart-display';
    - +
    `, diff --git a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/answer/src/app/app.ts index e48b2d647254..ffee00ea9c50 100644 --- a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/answer/src/app/app.ts @@ -13,9 +13,9 @@ import {CartDisplay} from './cart-display'; Cart: {{ cartStore.totalQuantity() }} items (\${{ cartStore.totalPrice() }})
    - +
    - +
    `, diff --git a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/app.ts b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/app.ts index f41c6d7f5e61..9bf7070cc96e 100644 --- a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/app.ts @@ -10,9 +10,7 @@ import {Component, ChangeDetectionStrategy} from '@angular/core';

    Signals with Services Demo

    -
    - Cart: Loading... items ($Loading...) -
    +
    Cart: Loading... items ($Loading...)
    diff --git a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/cart-display.ts b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/cart-display.ts index 8efa4741e0a3..6d0d4d7cbe47 100644 --- a/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/cart-display.ts +++ b/adev/src/content/tutorials/signals/steps/7-using-signals-with-services/src/app/cart-display.ts @@ -6,7 +6,7 @@ import {CartStore} from './cart-store'; template: `

    Shopping Cart Demo

    - +

    Add Products

    @@ -14,7 +14,7 @@ import {CartStore} from './cart-store';
    - +

    Cart Contents

    @if (cartStore.cartItems().length === 0) {

    Your cart is empty

    @@ -26,7 +26,7 @@ import {CartStore} from './cart-store';

    {{ item.name }}

    \${{ item.price }} each

    - +
    {{ item.quantity }} @@ -36,7 +36,7 @@ import {CartStore} from './cart-store';
    }
    - +

    Total Items: {{ cartStore.totalQuantity() }}

    Total: \${{ cartStore.totalPrice() }}

    diff --git a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md index 5f6dba3abc33..f5e03e5422b4 100644 --- a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md +++ b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md @@ -76,7 +76,7 @@ The host bindings automatically re-evaluate when the signals change - just like Update the app template to demonstrate the reactive directive: -```angular-ts +```angular-html template: `

    Directive with Signals

    diff --git a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/answer/src/app/app.ts index 33c458c848d3..4094437479ba 100644 --- a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/answer/src/app/app.ts @@ -8,17 +8,11 @@ import {HighlightDirective} from './highlight-directive';

    Directive with Signals

    -
    - Hover me - Blue highlight -
    +
    Hover me - Blue highlight
    -
    - Hover me - Green highlight -
    +
    Hover me - Green highlight
    -
    - Hover me - Yellow highlight -
    +
    Hover me - Yellow highlight
    `, styleUrl: './app.css', diff --git a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/src/app/app.ts b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/src/app/app.ts index 19836f2a0edf..c760949d46f2 100644 --- a/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/8-using-signals-with-directives/src/app/app.ts @@ -8,17 +8,11 @@ import {HighlightDirective} from './highlight-directive';

    Directive with Signals

    -
    - Hover me - Blue highlight -
    +
    Hover me - Blue highlight
    -
    - Hover me - Green highlight -
    +
    Hover me - Green highlight
    -
    - Hover me - Yellow highlight -
    +
    Hover me - Yellow highlight
    `, styleUrl: './app.css', diff --git a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/app.ts b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/app.ts index f30368c3286c..645b05495abb 100644 --- a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/app.ts @@ -26,10 +26,7 @@ import {ProductCard} from './product-card';
    - +
    diff --git a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/product-card.ts b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/product-card.ts index 63994e205c9e..bb3438ec92fa 100644 --- a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/product-card.ts +++ b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/answer/src/app/product-card.ts @@ -13,7 +13,8 @@ import {Component, input, signal, ChangeDetectionStrategy} from '@angular/core'; Hide } @else { Show - } Details + } + Details
    @if (showDetails()) { diff --git a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/app.ts b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/app.ts index e2cf59f806d4..4c81eb03bc3a 100644 --- a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/app.ts +++ b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/app.ts @@ -22,16 +22,13 @@ import {ProductCard} from './product-card'; [description]="'High-performance laptop'" [available]="true" [productId]="'LAP001'" - [category]="'Electronics'" + [category]="'Electronics'" />
    - - + +
    diff --git a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/product-card.ts b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/product-card.ts index 63994e205c9e..bb3438ec92fa 100644 --- a/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/product-card.ts +++ b/adev/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/src/app/product-card.ts @@ -13,7 +13,8 @@ import {Component, input, signal, ChangeDetectionStrategy} from '@angular/core'; Hide } @else { Show - } Details + } + Details
    @if (showDetails()) { diff --git a/adev/src/context/BUILD.bazel b/adev/src/context/BUILD.bazel index 149301463f22..1fba15af8fd1 100644 --- a/adev/src/context/BUILD.bazel +++ b/adev/src/context/BUILD.bazel @@ -6,7 +6,6 @@ filegroup( name = "context", srcs = glob([ "**/*.md", - "**/*.txt", "**/*.mdc", ]), visibility = ["//visibility:public"], diff --git a/adev/src/context/airules.md b/adev/src/context/airules.md index 258fa3fd02e1..fc15b336a775 100644 --- a/adev/src/context/airules.md +++ b/adev/src/context/airules.md @@ -25,26 +25,26 @@ export class {{ClassName}} { ```css .container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; - - button { - margin-top: 10px; - } + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + + button { + margin-top: 10px; + } } ``` ```html
    - @if (isServerRunning()) { - Yes, the server is running - } @else { - No, the server is not running - } - + @if (isServerRunning()) { + Yes, the server is running + } @else { + No, the server is not running + } +
    ``` diff --git a/adev/src/context/guidelines.md b/adev/src/context/guidelines.md index 4c41cf8661f2..df8b4ae5c011 100644 --- a/adev/src/context/guidelines.md +++ b/adev/src/context/guidelines.md @@ -25,26 +25,26 @@ export class {{ClassName}} { ```css .container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; - - button { - margin-top: 10px; - } + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + + button { + margin-top: 10px; + } } ``` ```html
    - @if (isServerRunning()) { - Yes, the server is running - } @else { - No, the server is not running - } - + @if (isServerRunning()) { + Yes, the server is running + } @else { + No, the server is not running + } +
    ``` @@ -78,8 +78,14 @@ Here is a link to the most recent Angular style guide https://angular.dev/style- - Do NOT set `standalone: true` inside the `@Component`, `@Directive` and `@Pipe` decorators - Use signals for state management - Implement lazy loading for feature routes -- Use `NgOptimizedImage` for all static images. - Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead +- Use `NgOptimizedImage` for all static images. + - `NgOptimizedImage` does not work for inline base64 images. + +### Accessibility Requirements + +- It MUST pass all AXE checks. +- It MUST follow all WCAG AA minimums, including focus management, color contrast, and ARIA attributes. ### Components @@ -104,8 +110,11 @@ Here is a link to the most recent Angular style guide https://angular.dev/style- - Keep templates simple and avoid complex logic - Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch` +- Do not assume globals like (`new Date()`) are available. +- Do not write arrow functions in templates (they are not supported). - Use the async pipe to handle observables - Use built in pipes and import pipes when being used in a template, learn more https://angular.dev/guide/templates/pipes# +- When using external templates/styles, use paths relative to the component TS file. ### Services diff --git a/adev/src/index.html b/adev/src/index.html index c000bd4d26fb..ef4f4f69dac9 100644 --- a/adev/src/index.html +++ b/adev/src/index.html @@ -11,10 +11,8 @@ const LIGHT_MODE_CLASS_NAME = 'docs-light-mode'; const PREFERS_COLOR_SCHEME_DARK = '(prefers-color-scheme: dark)'; - const theme = localStorage.getItem(THEME_PREFERENCE_LOCAL_STORAGE_KEY) ?? 'auto'; - const prefersDark = - window.matchMedia && window.matchMedia(PREFERS_COLOR_SCHEME_DARK).matches; + const prefersDark = window.matchMedia && window.matchMedia(PREFERS_COLOR_SCHEME_DARK).matches; const documentClassList = this.document.documentElement.classList; // clearing classes before setting them. @@ -25,7 +23,7 @@ documentClassList.add(LIGHT_MODE_CLASS_NAME); } - if(location.search.includes('uwu')) { + if (location.search.includes('uwu')) { documentClassList.add('uwu'); } diff --git a/adev/src/styles/_resets.scss b/adev/src/styles/_resets.scss index c56e260fed0d..634b23fd56b0 100644 --- a/adev/src/styles/_resets.scss +++ b/adev/src/styles/_resets.scss @@ -10,7 +10,6 @@ // page don't affect them, however they can be somewhat aggressive. To prevent them from // affecting structural styles like the CDK overlay's positioning CSS, we include // them in the global stylesheet, as early as possible. - *, code::before, code, pre, diff --git a/contributing-docs/coding-standards.md b/contributing-docs/coding-standards.md index 7b7cd83249e6..5c7d353e204c 100644 --- a/contributing-docs/coding-standards.md +++ b/contributing-docs/coding-standards.md @@ -144,8 +144,8 @@ and the return value: Boolean properties and return values should use "Whether..." as opposed to "True if...": ```ts - /** Whether the button is disabled. */ - disabled: boolean = false; +/** Whether the button is disabled. */ +disabled: boolean = false; ``` #### Try-Catch @@ -178,10 +178,10 @@ Use `readonly` members wherever possible. ```typescript /** NO: */ - class DefaultRouteReuseStrategy { } + class DefaultRouteReuseStrategy {} /** YES: */ - class NonStoringRouteReuseStrategy { } + class NonStoringRouteReuseStrategy {} ``` ##### Observables @@ -227,13 +227,20 @@ Give test classes and examples meaningful, descriptive names. ```ts /** PREFER: describes the scenario under test. */ -class FormGroupWithCheckboxAndRadios { /* ... */ } -class InputWithNgModel { /* ... */ } - +class FormGroupWithCheckboxAndRadios { + /* ... */ +} +class InputWithNgModel { + /* ... */ +} /** AVOID: does not fully describe the scenario under test. */ -class Comp { /* ... */ } -class InputComp { /* ... */ } +class Comp { + /* ... */ +} +class InputComp { + /* ... */ +} ``` #### RxJS @@ -259,7 +266,7 @@ describe('Router', () => { it('should not reuse routes upon location change', () => { // ... }); - }) + }); }); /** AVOID: does not fully describe the scenario under test. */ @@ -268,6 +275,6 @@ describe('Router', () => { it('should work', () => { // ... }); - }) + }); }); ``` diff --git a/contributing-docs/debugging-tips.md b/contributing-docs/debugging-tips.md index ab87889b4075..e5ece9071c4f 100644 --- a/contributing-docs/debugging-tips.md +++ b/contributing-docs/debugging-tips.md @@ -28,12 +28,13 @@ By default, the debug tools are disabled. You can enable debug tools as follows: import {ApplicationRef} from '@angular/core'; import {platformBrowser, enableDebugTools} from '@angular/platform-browser'; -platformBrowser().bootstrapModule(AppModule) - .then(moduleRef => { +platformBrowser() + .bootstrapModule(AppModule) + .then((moduleRef) => { const applicationRef = moduleRef.injector.get(ApplicationRef); const appComponent = applicationRef.components[0]; enableDebugTools(appComponent); - }) + }); ``` ### Using debug tools @@ -133,7 +134,7 @@ kinds of computation. Example: ```typescript @Component({ - template: '' + template: '', }) class FancyButton { // GOOD: no computation, just return the value @@ -141,7 +142,9 @@ class FancyButton { // BAD: computes the final value upon request _title: String; - get title(): String { return this._title.trim().toUpperCase(); } + get title(): String { + return this._title.trim().toUpperCase(); + } } ``` diff --git a/dev-app/BUILD.bazel b/dev-app/BUILD.bazel index 4cf887cbcf1e..5df24eacfb9b 100644 --- a/dev-app/BUILD.bazel +++ b/dev-app/BUILD.bazel @@ -39,7 +39,6 @@ ng_application( ], env = { "NG_BUILD_PARTIAL_SSR": "1", - "NG_FORCE_TTY": "0", }, ng_config = ":ng_config", node_modules = ":node_modules", @@ -59,7 +58,6 @@ ng_application( ], env = { "NG_BUILD_OPTIMIZE_CHUNKS": "1", - "NG_FORCE_TTY": "0", }, ng_config = ":ng_config", node_modules = ":node_modules", diff --git a/dev-app/angular.json b/dev-app/angular.json index 59a15350d3f0..3d32b861c50d 100644 --- a/dev-app/angular.json +++ b/dev-app/angular.json @@ -3,7 +3,10 @@ "version": 1, "cli": { "packageManager": "pnpm", - "analytics": "dca119a9-da31-47f7-a6cb-b60541037021" + "analytics": "dca119a9-da31-47f7-a6cb-b60541037021", + "cache": { + "enabled": false + } }, "newProjectRoot": "projects", "projects": { diff --git a/dev-app/package.json b/dev-app/package.json index 66083014d8da..ad5a42612cec 100644 --- a/dev-app/package.json +++ b/dev-app/package.json @@ -14,13 +14,13 @@ "@angular/platform-browser": "workspace:*", "@angular/platform-server": "workspace:*", "@angular/router": "workspace:*", - "@angular/ssr": "21.1.0-next.0", + "@angular/ssr": "21.1.0-next.2", "rxjs": "~7.8.0", "tslib": "^2.3.0" }, "devDependencies": { - "@angular/build": "21.1.0-next.0", - "@angular/cli": "21.1.0-next.0", + "@angular/build": "21.1.0-next.2", + "@angular/cli": "21.1.0-next.2", "@angular/compiler-cli": "workspace:*", "jsdom": "^27.0.0", "typescript": "~5.9.2", diff --git a/devtools/projects/ng-devtools-backend/src/lib/BUILD.bazel b/devtools/projects/ng-devtools-backend/src/lib/BUILD.bazel index b6910a571f62..f0d542b67ed0 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/BUILD.bazel +++ b/devtools/projects/ng-devtools-backend/src/lib/BUILD.bazel @@ -89,9 +89,6 @@ ts_test_library( ], deps = [ ":router_tree", - "//:node_modules/@angular/core", - "//:node_modules/@angular/router", - "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts b/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts index 096aeb01c8b7..7b7fca6adc98 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts @@ -54,12 +54,7 @@ import {unHighlight} from './highlighter'; import {disableTimingAPI, enableTimingAPI, initializeOrGetDirectiveForestHooks} from './hooks'; import {start as startProfiling, stop as stopProfiling} from './hooks/capture'; import {ComponentTreeNode} from './interfaces'; -import { - getElementRefByName, - getComponentRefByName, - parseRoutes, - RoutePropertyType, -} from './router-tree'; +import {getRouterCallableConstructRef, parseRoutes, RoutePropertyType} from './router-tree'; import {ngDebugClient, ngDebugDependencyInjectionApiIsSupported} from './ng-debug-api/ng-debug-api'; import {setConsoleReference} from './set-console-reference'; import {serializeDirectiveState, serializeValue} from './state-serializer/state-serializer'; @@ -203,24 +198,18 @@ const navigateRouteCallback = (messageBus: MessageBus) => (path: string) /** * Opens the source code of a component or a directive in the editor. - * @param name - The name of the component, provider, or directive to view source for. + * @param constructName - The name of the class/function that represents a component, provider, guard + * or other callable to view source for. * @param type - The type of the element to view source for component, provider, or directive. * @returns - The element instance of the component, provider, or directive. */ -export const viewSourceFromRouter = (name: string, type: RoutePropertyType) => { +export const viewSourceFromRouter = (constructName: string, type: RoutePropertyType) => { const router: any = getRouterInstance(); - if (router == null) { + if (router === null) { return; } - - let element; - if (type === 'component') { - element = getComponentRefByName(router.config, name); - } else { - element = getElementRefByName(type, router.config, name); - } - return element; + return getRouterCallableConstructRef(router.config, type, constructName); }; const startProfilingCallback = (messageBus: MessageBus) => () => @@ -350,13 +339,15 @@ const checkForAngular = (messageBus: MessageBus): void => { initializeOrGetDirectiveForestHooks(); } + const devMode = appIsAngularInDevMode(); + messageBus.emit('ngAvailability', [ { version: ngVersion.toString(), - devMode: appIsAngularInDevMode(), + devMode, ivy: appIsIvy, hydration: isHydrationEnabled(), - supportedApis: getSupportedApis(), + supportedApis: devMode ? getSupportedApis() : null, }, ]); }; diff --git a/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts b/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts index 79f565d9112d..58f24930f915 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {parseRoutes} from './router-tree'; +import {getRouterCallableConstructRef, parseRoutes} from './router-tree'; describe('parseRoutes', () => { it('should work without any routes', () => { @@ -16,7 +16,6 @@ describe('parseRoutes', () => { component: 'App Root', path: 'App Root', children: [], - data: [], isAux: false, isLazy: false, isActive: true, @@ -31,7 +30,6 @@ describe('parseRoutes', () => { expect(parsedRoutes).toEqual({ 'component': 'App Root', 'path': 'App Root', - 'data': [], 'children': [], 'isAux': false, 'isLazy': false, @@ -121,10 +119,8 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/(outlet:component-one)', 'pathMatch': undefined, - 'data': [], 'isAux': true, 'isLazy': false, 'isActive': false, @@ -136,11 +132,10 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/component-two', 'pathMatch': undefined, 'title': 'Component Two', - 'data': [{'key': 'name', 'value': 'component-two'}], + 'data': {name: 'component-two'}, 'isAux': false, 'isLazy': false, 'isActive': false, @@ -152,11 +147,9 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/component-two/component-two-one', 'pathMatch': undefined, 'title': '[Function]', - 'data': [], 'isAux': false, 'isLazy': false, 'isActive': false, @@ -168,11 +161,9 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/component-two/component-two-two', 'pathMatch': undefined, - 'title': 'titleResolver()', - 'data': [], + 'title': 'titleResolver', 'isAux': false, 'isLazy': false, 'isActive': false, @@ -186,10 +177,8 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/lazy', 'pathMatch': undefined, - 'data': [], 'isAux': false, 'isLazy': true, 'isActive': false, @@ -201,10 +190,8 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/redirect', 'pathMatch': undefined, - 'data': [], 'isAux': false, 'isLazy': false, 'isActive': false, @@ -217,10 +204,8 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/redirect-fn', 'pathMatch': undefined, - 'data': [], 'isAux': false, 'isLazy': false, 'isActive': false, @@ -233,19 +218,16 @@ describe('parseRoutes', () => { 'canMatchGuards': [], 'canDeactivateGuards': [], 'providers': [], - 'resolvers': [], 'path': '/redirect-named-fn', 'pathMatch': undefined, - 'data': [], 'isAux': false, 'isLazy': false, 'isActive': false, - 'redirectTo': 'redirectResolver()', + 'redirectTo': 'redirectResolver', }, ], 'isAux': false, 'isLazy': false, - 'data': [], 'isActive': true, } as any); }); @@ -267,7 +249,7 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].canActivateGuards).toEqual(['canActivateGuard()']); + expect(parsedRoutes.children![0].canActivateGuards).toEqual(['canActivateGuard']); }); it('should handle guards with arrow functions', () => { @@ -285,7 +267,7 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].canActivateGuards).toEqual(['arrowGuard()']); + expect(parsedRoutes.children![0].canActivateGuards).toEqual(['arrowGuard']); }); it('should handle guards with class instances', () => { @@ -342,11 +324,11 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); expect(parsedRoutes.children![0].canActivateGuards).toEqual([ - 'canActivateGuard()', + 'canActivateGuard', '[Function]', '[Function]', ]); - expect(parsedRoutes.children![0].canMatchGuards).toEqual(['canMatchGuard()']); + expect(parsedRoutes.children![0].canMatchGuards).toEqual(['canMatchGuard']); expect(parsedRoutes.children![0].canDeactivateGuards).toEqual(['CanDeactivateGuard']); }); @@ -365,7 +347,7 @@ describe('parseRoutes', () => { }; const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].matcher).toEqual('customMatcher()'); + expect(parsedRoutes.children![0].matcher).toEqual('customMatcher'); expect(parsedRoutes.children![0].path).toEqual('[Matcher]'); }); @@ -406,7 +388,7 @@ describe('parseRoutes', () => { }; const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].runGuardsAndResolvers).toEqual('customRerunLogic()'); + expect(parsedRoutes.children![0].runGuardsAndResolvers).toEqual('customRerunLogic'); }); it('should handle resolvers with named functions', () => { @@ -428,7 +410,7 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].resolvers).toEqual([{key: 'user', value: 'userResolver()'}]); + expect(parsedRoutes.children![0].resolvers).toEqual({user: 'userResolver'}); }); it('should handle resolvers with arrow functions', () => { @@ -448,7 +430,7 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].resolvers).toEqual([{key: 'data', value: 'dataResolver()'}]); + expect(parsedRoutes.children![0].resolvers).toEqual({data: 'dataResolver'}); }); it('should handle multiple resolvers on a single route', () => { @@ -478,11 +460,11 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].resolvers).toEqual([ - {key: 'user', value: 'userResolver()'}, - {key: 'settings', value: 'settingsResolver()'}, - {key: 'permissions', value: 'PermissionsResolver'}, - ]); + expect(parsedRoutes.children![0].resolvers).toEqual({ + user: 'userResolver', + settings: 'settingsResolver', + permissions: 'PermissionsResolver', + }); }); it('should handle nested routes with resolvers', () => { @@ -516,11 +498,143 @@ describe('parseRoutes', () => { const parsedRoutes = parseRoutes(nestedRouter as any); - expect(parsedRoutes.children![0].resolvers).toEqual([ - {key: 'parentData', value: 'parentResolver()'}, - ]); - expect(parsedRoutes.children![0].children![0].resolvers).toEqual([ - {key: 'childData', value: 'childResolver()'}, - ]); + expect(parsedRoutes.children![0].resolvers).toEqual({parentData: 'parentResolver'}); + expect(parsedRoutes.children![0].children![0].resolvers).toEqual({ + childData: 'childResolver', + }); + }); +}); + +describe('getRouterCallableConstructRef', () => { + class MockComponent {} + class MockService {} + function mockResolver() {} + function mockTitle() {} + function mockRedirectTo() {} + function mockMatcher() {} + function mockRunGuardsAndResolvers() {} + function mockCanActivate() {} + function mockCanDeactivate() {} + class MockCanActivateChild {} + class MockCanMatch {} + + const MOCK_ROUTES = [ + { + path: '', + providers: [MockService], + _loadedRoutes: [ + { + path: 'foo', + component: MockComponent, + children: [ + { + path: 'foo', + resolve: { + auth: mockResolver, + }, + }, + { + path: 'bar', + redirectTo: mockRedirectTo as any, + _loadedRoutes: [ + { + path: 'foo', + title: mockTitle as any, + }, + { + path: 'bar', + runGuardsAndResolvers: mockRunGuardsAndResolvers as any, + }, + { + path: 'baz', + canActivate: [mockCanActivate], + canActivateChild: [MockCanActivateChild], + canDeactivate: [mockCanDeactivate], + }, + { + path: 'qux', + canMatch: [MockCanMatch], + }, + ], + }, + { + path: 'baz', + matcher: mockMatcher as any, + }, + ], + }, + { + path: 'bar', + }, + ], + }, + ]; + + it(`should return null if the callable doesn't exist`, () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'component', 'NonExistent'); + expect(ref).toEqual(null); + }); + + it('should return null if there is a callable with the provided name but wrongly typed', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'providers', 'MockComponent'); + expect(ref).toEqual(null); + }); + + it('should find a component class', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'component', 'MockComponent'); + expect(ref).toEqual(MockComponent); + }); + + it('should find a resolver function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'resolvers', 'mockResolver'); + expect(ref).toEqual(mockResolver); + }); + + it('should find a title function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'title', 'mockTitle'); + expect(ref).toEqual(mockTitle); + }); + + it('should find a redirectTo function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'redirectTo', 'mockRedirectTo'); + expect(ref).toEqual(mockRedirectTo); + }); + + it('should find a matcher function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'matcher', 'mockMatcher'); + expect(ref).toEqual(mockMatcher); + }); + + it('should find a runGuardsAndResolvers function', () => { + const ref = getRouterCallableConstructRef( + MOCK_ROUTES, + 'runGuardsAndResolvers', + 'mockRunGuardsAndResolvers', + ); + expect(ref).toEqual(mockRunGuardsAndResolvers); + }); + + it('should find a canActivate function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'canActivate', 'mockCanActivate'); + expect(ref).toEqual(mockCanActivate); + }); + + it('should find a canDeactivate function', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'canDeactivate', 'mockCanDeactivate'); + expect(ref).toEqual(mockCanDeactivate); + }); + + it('should find a canActivateChild class', () => { + const ref = getRouterCallableConstructRef( + MOCK_ROUTES, + 'canActivateChild', + 'MockCanActivateChild', + ); + expect(ref).toEqual(MockCanActivateChild); + }); + + it('should find a canMatch class', () => { + const ref = getRouterCallableConstructRef(MOCK_ROUTES, 'canMatch', 'MockCanMatch'); + expect(ref).toEqual(MockCanMatch); }); }); diff --git a/devtools/projects/ng-devtools-backend/src/lib/router-tree.ts b/devtools/projects/ng-devtools-backend/src/lib/router-tree.ts index 7580dbad0f75..566cb340ffec 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/router-tree.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/router-tree.ts @@ -21,8 +21,6 @@ export type RoutePropertyType = export type RouteGuard = 'canActivate' | 'canActivateChild' | 'canDeactivate' | 'canMatch'; -const routeGuards = ['canActivate', 'canActivateChild', 'canDeactivate', 'canMatch']; - type Routes = any; type Router = any; @@ -87,7 +85,6 @@ export function parseRoutes(router: Router): Route { isAux: false, isLazy: false, isActive: true, // Root is always active. - data: [], }; return root; @@ -136,8 +133,6 @@ function assignChildrenToParent( canDeactivateGuards: getGuardNames(child, 'canDeactivate'), providers: getProviderName(child), path: routePath, - data: [], - resolvers: [], isAux, isLazy, isActive, @@ -171,25 +166,15 @@ function assignChildrenToParent( } if (child.resolve) { - for (const el in child.resolve) { - if (child.resolve.hasOwnProperty(el)) { - routeConfig?.resolvers?.push({ - key: el, - value: getClassOrFunctionName(child.resolve[el]), - }); - } + routeConfig.resolvers = {}; + + for (const [name, resolver] of Object.entries(child.resolve)) { + routeConfig.resolvers[name] = getClassOrFunctionName(resolver); } } if (child.data) { - for (const el in child.data) { - if (child.data.hasOwnProperty(el)) { - routeConfig?.data?.push({ - key: el, - value: child.data[el], - }); - } - } + routeConfig.data = child.data; } return routeConfig; @@ -217,11 +202,7 @@ function getClassOrFunctionName(fn: Function, defaultName?: string) { return '[Function]'; } - // Check if it's a class by examining the function's string representation - const isClass = /^class\s/.test(fn.toString()); - - // Return class name without parentheses, function name with parentheses - return isClass ? fn.name : `${fn.name}()`; + return fn.name; } function getPropertyName( @@ -246,104 +227,75 @@ function childRouteName(child: AngularRoute): string { } /** - * Get the element reference by type & name from the routes array. Called recursively to search through all children. - * @param type - type of element to search for (canActivate, canActivateChild, canDeactivate, canLoad, providers, redirectTo , title) - * @param routes - array of routes to search through - * @param name - name of the element to search for refers to the name of the guard or provider - * @returns - the element reference if found, otherwise null + * Find a named callable construct (class or function) by type & name in a source routes tree. + * + * @param routes - Source routes tree to search through. + * @param type - Type of construct to search for (e.g. canActivate, providers, redirectTo, title, etc.). + * @param constructName - Name of the construct to search for. + * @returns - The callable construct reference (`Function`) if found; otherwise, `null`. */ -export function getElementRefByName( - type: RoutePropertyType, +export function getRouterCallableConstructRef( routes: AngularRoute[], - name: string, -): any | null { - for (const element of routes) { - if (type === 'resolvers' && element.resolve) { - for (const key in element.resolve) { - if (element.resolve.hasOwnProperty(key)) { - const functionName = getClassOrFunctionName(element.resolve[key]); - //TODO: improve this, not every ResolverFn has a name property - if (functionName === name) { - return element.resolve[key]; + type: RoutePropertyType, + constructName: string, +): Function | null { + for (const route of routes) { + switch (type) { + case 'component': + if (route.component?.name === constructName) { + return route.component; + } + break; + + case 'resolvers': + if (route.resolve) { + for (const key of Object.keys(route.resolve)) { + const functionName = getClassOrFunctionName(route.resolve[key]); + if (functionName === constructName) { + return route.resolve[key]; + } } } - } - } - - const functionProperties: Exclude[] = [ - 'title', - 'redirectTo', - 'matcher', - 'runGuardsAndResolvers', - ]; - - for (const property of functionProperties) { - if (type === property && element[property] instanceof Function) { - const functionName = getClassOrFunctionName(element[property]); - // TODO: improve this, not every function has a name property - if (functionName === name) { - return element[property]; + break; + + case 'title': + case 'redirectTo': + case 'matcher': + case 'runGuardsAndResolvers': + if (route[type] instanceof Function) { + const functionName = getClassOrFunctionName(route[type]); + if (functionName === constructName) { + return route[type]; + } } - } - } - - if (routeGuards.includes(type)) { - const routeGuard = type as RouteGuard; - if (element[routeGuard]) { - for (const guard of element[routeGuard]) { - // TODO: improve this, not every guard has a name property - if ((guard as any).name === name) { - return guard; + break; + + case 'canActivate': + case 'canActivateChild': + case 'canDeactivate': + case 'canMatch': + case 'providers': + if (route[type]) { + for (const callable of route[type]) { + if (callable instanceof Function && callable.name === constructName) { + return callable; + } } } - } + break; } - // _loadedRoutes is internal, we can't acess it with the dot notation - const loadedRoutes = (element as any)?.['_loadedRoutes'] as AngularRoute[] | undefined; - if (loadedRoutes) { - const result = getElementRefByName(type, loadedRoutes, name); - if (result !== null) { - return result; - } - } + // _loadedRoutes is internal, we can't access it with the dot notation + const loadedRoutes = (route as any)['_loadedRoutes'] as AngularRoute[] | undefined; + const childrenRoutes = loadedRoutes || route.children; - if (element?.children) { - const result = getElementRefByName(type, element.children, name); + if (childrenRoutes) { + const result = getRouterCallableConstructRef(childrenRoutes, type, constructName); if (result !== null) { return result; } } } -} - -/** - * Get the componet reference by name from the routes array. Called recursively to search through all children. - * @param routes - array of routes to search through - * @param name - name of the component to search for - * @returns - the element reference if found, otherwise null - */ -export function getComponentRefByName(routes: AngularRoute[], name: string): any | null { - for (const element of routes) { - if (element?.component?.name === name) { - return element.component; - } - - // _loadedRoutes is internal, we can't acess it with the dot notation - const loadedRoutes = (element as any)?.['_loadedRoutes'] as AngularRoute[] | undefined; - if (loadedRoutes) { - const result = getComponentRefByName(loadedRoutes, name); - if (result !== null) { - return result; - } - } - if (element?.children) { - const result = getComponentRefByName(element.children, name); - if (result !== null) { - return result; - } - } - } return null; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel index 2c9a0bb4495f..34f0e895464e 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel @@ -32,6 +32,7 @@ ng_project( "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph:signal-graph-manager", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view:signals-tab", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/ng-devtools/src/lib/shared/split", "//devtools/projects/ng-devtools/src/lib/shared/split:responsive-split", "//devtools/projects/protocol", @@ -54,6 +55,7 @@ ts_test_library( "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph:signal-graph-manager", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts index 0929ddad7153..80fbb911be7d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts @@ -41,10 +41,8 @@ import {FlatNode} from './directive-forest/component-data-source'; import {DirectiveForestComponent} from './directive-forest/directive-forest.component'; import {IndexedNode} from './directive-forest/index-forest'; import {constructPathOfKeysToPropertyValue} from './property-resolver/directive-property-resolver'; -import { - ElementPropertyResolver, - FlatNode as PropertyFlatNode, -} from './property-resolver/element-property-resolver'; +import {ElementPropertyResolver} from './property-resolver/element-property-resolver'; +import {FlatNode as PropertyFlatNode} from '../../shared/object-tree-explorer/object-tree-types'; import {PropertyTabComponent} from './property-tab/property-tab.component'; import {FormsModule} from '@angular/forms'; import {Platform} from '@angular/cdk/platform'; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts index e3c465bb1caf..3396474ecb2d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts @@ -19,10 +19,11 @@ import SpyObj = jasmine.SpyObj; import {By} from '@angular/platform-browser'; import {FrameManager} from '../../application-services/frame_manager'; import {Component, CUSTOM_ELEMENTS_SCHEMA, output, input} from '@angular/core'; -import {ElementPropertyResolver, FlatNode} from './property-resolver/element-property-resolver'; +import {ElementPropertyResolver} from './property-resolver/element-property-resolver'; import {BreadcrumbsComponent} from './directive-forest/breadcrumbs/breadcrumbs.component'; import {PropertyTabComponent} from './property-tab/property-tab.component'; import {SignalGraphManager} from './signal-graph/signal-graph-manager'; +import {FlatNode} from '../../shared/object-tree-explorer/object-tree-types'; @Component({ selector: 'ng-directive-forest', diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html index f958e2aea051..5e9606d64d5b 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html @@ -14,7 +14,7 @@ @if (currentMatch(); as currentMatch) { {{ currentMatch }} of {{ matchesCount() }} } @else { - {{ matchesCount() === 1 ? "1 match" : `${matchesCount()} matches` }} + {{ matchesCount() === 1 ? '1 match' : `${matchesCount()} matches` }} }
    diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/tree-node/tree-node.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/tree-node/tree-node.component.html index fa0cb25a8d89..ff1a7157b2fc 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/tree-node/tree-node.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/tree-node/tree-node.component.html @@ -40,7 +40,7 @@ } @if (defer && defer.currentBlock) { - (@{{defer.currentBlock}}) + (@{{ defer.currentBlock }}) } @switch (hydration?.status) { @@ -73,11 +73,11 @@
    @if (hydration.expectedNodeDetails) {
    Expected DOM:
    -
    {{hydration.expectedNodeDetails}}
    +
    {{ hydration.expectedNodeDetails }}
    } @if (hydration.actualNodeDetails) {
    Actual DOM:
    -
    {{hydration.actualNodeDetails}}
    +
    {{ hydration.actualNodeDetails }}
    }
    } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/BUILD.bazel index 08fcb274b2dc..836dc10f9a55 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/BUILD.bazel @@ -19,6 +19,7 @@ ng_project( "//:node_modules/rxjs", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/diffing", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) @@ -37,6 +38,7 @@ ts_test_library( "//:node_modules/@angular/core", "//:node_modules/@angular/material", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts index 5488404b5e94..03ce37cb3e11 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts @@ -7,8 +7,7 @@ */ import {Descriptor} from '../../../../../../protocol'; - -import {Property} from './element-property-resolver'; +import {Property} from '../../../shared/object-tree-explorer/object-tree-types'; export const arrayifyProps = ( props: {[prop: string]: Descriptor} | Descriptor[], diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts index 010598723b4b..9caee2453d84 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts @@ -17,10 +17,10 @@ import { Properties, } from '../../../../../../protocol'; -import {FlatNode, Property} from './element-property-resolver'; import {getTreeFlattener} from './flatten'; import {PropertyDataSource} from './property-data-source'; import {getExpandedDirectiveProperties} from './property-expanded-directive-properties'; +import {FlatNode, Property} from '../../../shared/object-tree-explorer/object-tree-types'; export interface DirectiveTreeData { dataSource: PropertyDataSource; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/element-property-resolver.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/element-property-resolver.ts index 09d5cf383957..605fdf2d418d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/element-property-resolver.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/element-property-resolver.ts @@ -9,7 +9,6 @@ import {Injectable} from '@angular/core'; import { ComponentExplorerViewProperties, - Descriptor, DirectivePosition, DirectivesProperties, Events, @@ -20,18 +19,6 @@ import {IndexedNode} from '../directive-forest/index-forest'; import {DirectivePropertyResolver} from './directive-property-resolver'; -export interface FlatNode { - expandable: boolean; - prop: Property; - level: number; -} - -export interface Property { - name: string; - descriptor: Descriptor; - parent: Property | null; -} - @Injectable() export class ElementPropertyResolver { private _directivePropertiesController = new Map(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/flatten.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/flatten.ts index 9fca59fc19d3..79c2dac0b3f4 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/flatten.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/flatten.ts @@ -11,7 +11,7 @@ import {Descriptor, PropType} from '../../../../../../protocol'; import {Observable} from 'rxjs'; import {arrayifyProps} from './arrayify-props'; -import {FlatNode, Property} from './element-property-resolver'; +import {FlatNode, Property} from '../../../shared/object-tree-explorer/object-tree-types'; export const getTreeFlattener = () => new MatTreeFlattener( diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.spec.ts index da3b95a0b92c..40fadda05967 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.spec.ts @@ -9,9 +9,9 @@ import {FlatTreeControl} from '@angular/cdk/tree'; import {PropType} from '../../../../../../protocol'; -import {FlatNode} from './element-property-resolver'; import {getTreeFlattener} from './flatten'; import {PropertyDataSource} from './property-data-source'; +import {FlatNode} from '../../../shared/object-tree-explorer/object-tree-types'; const flatTreeControl = new FlatTreeControl( (node) => node.level, diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts index b1b8276f557f..4c399a2a82c5 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts @@ -23,7 +23,7 @@ import {map} from 'rxjs/operators'; import {diff} from '../../diffing'; import {arrayifyProps} from './arrayify-props'; -import {FlatNode, Property} from './element-property-resolver'; +import {FlatNode, Property} from '../../../shared/object-tree-explorer/object-tree-types'; const trackBy: TrackByFunction = (_: number, item: FlatNode) => `#${item.prop.name}#${item.prop.descriptor.preview}#${item.level}`; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts index 0907972bcd62..80332f3fc70e 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts @@ -7,8 +7,7 @@ */ import {Descriptor, NestedProp, PropType} from '../../../../../../protocol'; - -import {FlatNode} from './element-property-resolver'; +import {FlatNode} from '../../../shared/object-tree-explorer/object-tree-types'; export const getExpandedDirectiveProperties = (data: FlatNode[]): NestedProp[] => { const getChildren = (prop: Descriptor) => { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel index 8da51ca14f76..86c37b9dc7c0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel @@ -19,10 +19,10 @@ ng_project( deps = [ "//:node_modules/@angular/core", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view/defer-view.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view/defer-view.component.html index 765123a89811..28d83da4696d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view/defer-view.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view/defer-view.component.html @@ -1,6 +1,6 @@ Current block
    - @{{defer().currentBlock ?? 'defer'}} + @{{ defer().currentBlock ?? 'defer' }}
    @let triggers = defer().triggers; @@ -10,11 +10,15 @@ @if (blocks.placeholderBlock) { @placeholder{{blocks.placeholderBlock.minimumTime ? `(minimum ${blocks.placeholderBlock.minimumTime} ms)` : ''}}{{ + blocks.placeholderBlock.minimumTime + ? `(minimum ${blocks.placeholderBlock.minimumTime} ms)` + : '' + }} } @if (blocks.loadingBlock) { - @loading{{loadingBlockInfo()}} + @loading{{ loadingBlockInfo() }} } @if (blocks.hasErrorBlock) { @error @@ -25,18 +29,18 @@

    Defer triggers

    @for (trigger of triggers.defer; track $index) { - {{trigger}} + {{ trigger }} } @if (triggers.hydrate.length > 0) {

    Hydration triggers

    @for (trigger of triggers.hydrate; track $index) { - {{trigger}} + {{ trigger }} } } @if (triggers.prefetch.length > 0) {

    Prefetch triggers

    @for (trigger of triggers.prefetch; track $index) { - {{trigger}} + {{ trigger }} } } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts index eb435975a266..5683fd7d900b 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts @@ -10,10 +10,10 @@ import {ChangeDetectionStrategy, Component, computed, input, output} from '@angu import {DebugSignalGraphNode, DirectivePosition} from '../../../../../../protocol'; import {IndexedNode} from '../directive-forest/index-forest'; -import {FlatNode} from '../property-resolver/element-property-resolver'; import {PropertyTabHeaderComponent} from './property-tab-header/property-tab-header.component'; import {DeferViewComponent} from './defer-view/defer-view.component'; import {PropertyViewComponent} from './property-view/property-view.component'; +import {FlatNode} from '../../../shared/object-tree-explorer/object-tree-types'; @Component({ selector: 'ng-property-tab', diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel index 4d93c8d09b25..4bdd36429afc 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel @@ -21,6 +21,7 @@ ng_project( "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/BUILD.bazel index 85ce32757960..61929532d45d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/BUILD.bazel @@ -6,6 +6,7 @@ sass_binary( name = "property-view-body_styles", src = "property-view-body.component.scss", deps = [ + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:prop-action-btn", "//devtools/projects/ng-devtools/src/styles:typography", ], ) @@ -22,10 +23,13 @@ ng_project( deps = [ "//:node_modules/@angular/core", "//:node_modules/@angular/material", + "//devtools/projects/ng-devtools/src/lib/application-providers:supported_apis", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/dependency-viewer", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree", + "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph:signal-graph-manager", "//devtools/projects/ng-devtools/src/lib/shared/docs-ref-button", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.html index f6d19abfe9f9..17a9afa579de 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.html @@ -25,14 +25,46 @@ {{ panel.title() }} - + [editingEnabled]="true" + (propInspect)="handleInspect($event)" + (propValueUpdate)="updateValue($event)" + > + + @if ( + supportedApis().signalPropertiesInspection && + signalGraphEnabled() && + getSignalNode(node); + as signalNode + ) { + @let graphBtnTooltip = `Show '${node.prop.name}' signal graph`; + + } + + @if (node.level === 0) { + @let tooltip = "Log '" + node.prop.name + "' to console"; + + } + +
    } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.scss index 168e90667cac..ab41c70a248c 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.scss +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.scss @@ -1,4 +1,5 @@ @use '../../../../../../styles/typography'; +@use '../../../../../shared/object-tree-explorer/prop-action-btn'; :host { ng-docs-ref-button { @@ -12,6 +13,24 @@ overflow: hidden; } + .mat-accordion-content { + &:not(:empty) { + border-top: 1px solid var(--color-separator); + } + + ng-object-tree-explorer { + --ote-row-indent: 0.25rem; + + .show-signal { + mat-icon { + width: 14px; + height: 14px; + font-size: 14px; + } + } + } + } + /* FRAGILE */ ::ng-deep { mat-expansion-panel { @@ -58,8 +77,4 @@ } } } - - .mat-accordion-content:not(:empty) { - border-top: 1px solid var(--color-separator); - } } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.ts index 7cedc89b328c..17d5da9151c9 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-body.component.ts @@ -6,41 +6,53 @@ * found in the LICENSE file at https://angular.dev/license */ -import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; import { ChangeDetectionStrategy, Component, ɵFramework as Framework, computed, + inject, input, output, signal, } from '@angular/core'; -import {DebugSignalGraphNode, DirectivePosition} from '../../../../../../../../protocol'; +import {MatIcon} from '@angular/material/icon'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatExpansionModule} from '@angular/material/expansion'; +import {DebugSignalGraphNode, DirectivePosition} from '../../../../../../../../protocol'; import { DirectivePropertyResolver, DirectiveTreeData, } from '../../../property-resolver/directive-property-resolver'; -import {FlatNode} from '../../../property-resolver/element-property-resolver'; -import {PropertyViewTreeComponent} from './property-view-tree/property-view-tree.component'; -import {MatExpansionModule} from '@angular/material/expansion'; import {DependencyViewerComponent} from './dependency-viewer/dependency-viewer.component'; import {DocsRefButtonComponent} from '../../../../../shared/docs-ref-button/docs-ref-button.component'; +import {ObjectTreeExplorerComponent} from '../../../../../shared/object-tree-explorer/object-tree-explorer.component'; +import {SUPPORTED_APIS} from '../../../../../application-providers/supported_apis'; + +import {SignalGraphManager} from '../../../signal-graph/signal-graph-manager'; +import {FlatNode} from '../../../../../shared/object-tree-explorer/object-tree-types'; @Component({ selector: 'ng-property-view-body', templateUrl: './property-view-body.component.html', styleUrls: ['./property-view-body.component.scss'], imports: [ + MatIcon, + MatTooltip, MatExpansionModule, - PropertyViewTreeComponent, DocsRefButtonComponent, DependencyViewerComponent, + ObjectTreeExplorerComponent, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class PropertyViewBodyComponent { + private readonly signalGraph = inject(SignalGraphManager); + protected readonly supportedApis = inject(SUPPORTED_APIS); + + protected readonly signalGraphEnabled = () => this.supportedApis().signals; + readonly controller = input.required(); readonly directiveInputControls = input.required(); readonly directivePropControls = input.required(); @@ -89,7 +101,8 @@ export class PropertyViewBodyComponent { this.controller().updateValue(node, newValue); } - logValue(node: FlatNode): void { + logValue(e: Event, node: FlatNode): void { + e.stopPropagation(); this.controller().logValue(node); } @@ -99,4 +112,11 @@ export class PropertyViewBodyComponent { directivePosition: this.controller().directivePosition, }); } + + getSignalNode(node: FlatNode): DebugSignalGraphNode | null { + if (node.prop.descriptor.containerType?.includes('Signal')) { + return this.signalGraph.graph()?.nodes.find((sn) => sn.label === node.prop.name) ?? null; + } + return null; + } } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/BUILD.bazel deleted file mode 100644 index ea3f19ccfc90..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("//devtools/tools:defaults.bzl", "ng_project", "sass_binary") - -package(default_visibility = ["//devtools:__subpackages__"]) - -sass_binary( - name = "property-view-tree_styles", - src = "property-view-tree.component.scss", - deps = [ - "//devtools/projects/ng-devtools/src/styles:typography", - ], -) - -ng_project( - name = "property-view-tree", - srcs = [ - "property-view-tree.component.ts", - ], - angular_assets = [ - "property-view-tree.component.html", - ":property-view-tree_styles", - ], - deps = [ - "//:node_modules/@angular/cdk", - "//:node_modules/@angular/common", - "//:node_modules/@angular/core", - "//:node_modules/@angular/material", - "//devtools/projects/ng-devtools/src/lib/application-providers:supported_apis", - "//devtools/projects/ng-devtools/src/lib/application-services:settings", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph:signal-graph-manager", - "//devtools/projects/protocol", - ], -) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.html deleted file mode 100644 index 3a752e49c4b7..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.html +++ /dev/null @@ -1,75 +0,0 @@ -@if (treeControl()) { - - -
    - {{ node.prop.name }}: - @if (!node.prop.descriptor.editable) { - - } @else { - - } - - @if (node.level === 0) { - - } -
    -
    - - - -
    -} - - - @if ( - supportedApis().signalPropertiesInspection && signalGraphEnabled() && getSignalNode(node); - as signalNode - ) { - @let tooltip = 'Show \'' + node.prop.name + '\' signal graph'; - - } - - - - @let tooltip = 'Log \'' + node.prop.name + '\' to console'; - - diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.scss deleted file mode 100644 index 459e57a2e86b..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.scss +++ /dev/null @@ -1,97 +0,0 @@ -@use '../../../../../../../styles/typography'; - -:host { - width: 100%; - display: block; - overflow: auto; - - .property-list { - margin: 0.5rem; - } - - .prop-row { - $margin-left: 1.25rem; - $second-row-text-indent: 1rem; - - @extend %monospaced; - display: inline; - text-indent: -$second-row-text-indent; - margin-left: $margin-left + $second-row-text-indent; - - &.expandable-row { - position: relative; - border: none; - background: none; - padding: 0; - text-align: left; - - .expand-icon { - position: absolute; - font-size: 12px; - width: 12px; - height: 12px; - top: 0.175rem; - left: -0.875rem - $second-row-text-indent; - text-indent: 0; - cursor: pointer; - } - } - } - - mat-tree-node { - min-height: 1.375rem !important; - cursor: default; - } - - .name { - color: var(--color-tree-node-element-name); - } - - .value:not(:last-child) { - margin-right: 0.25rem; - } - - .prop-action-btn { - position: relative; - border-radius: 50%; - flex: 0 0 16px; - width: 16px; - height: 16px; - border: none; - display: inline-flex; - align-items: center; - justify-content: center; - color: var(--quaternary-contrast); - background-color: transparent; - vertical-align: middle; - padding: 0; - top: -1px; - cursor: pointer; - - &:not(:last-child) { - margin-right: 0.25rem; - } - - &:hover { - $color: color-mix(in srgb, var(--dynamic-blue-02) 80%, var(--full-contrast) 20%); - background: $color; - outline: 2px solid $color; - color: var(--septenary-contrast); - } - - mat-icon { - width: 16px; - height: 16px; - font-size: 16px; - top: 0; - } - - &.show-signal { - mat-icon { - width: 14px; - height: 14px; - font-size: 14px; - } - } - } -} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.ts deleted file mode 100644 index 5cb8078fe0f9..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-view-tree.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ChangeDetectionStrategy, Component, inject, input, output} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {MatTooltip} from '@angular/material/tooltip'; -import {MatIcon} from '@angular/material/icon'; -import {FlatTreeControl} from '@angular/cdk/tree'; - -import {FlatNode} from '../../../../property-resolver/element-property-resolver'; -import {PropertyDataSource} from '../../../../property-resolver/property-data-source'; -import {PropertyEditorComponent} from './property-editor/property-editor.component'; -import {PropertyPreviewComponent} from './property-preview/property-preview.component'; -import {MatTree, MatTreeNode, MatTreeNodeDef, MatTreeNodePadding} from '@angular/material/tree'; -import {SUPPORTED_APIS} from '../../../../../../application-providers/supported_apis'; -import {SignalGraphManager} from '../../../../signal-graph/signal-graph-manager'; -import {DebugSignalGraphNode} from '../../../../../../../../../protocol'; -import {Settings} from '../../../../../../application-services/settings'; - -@Component({ - selector: 'ng-property-view-tree', - templateUrl: './property-view-tree.component.html', - styleUrls: ['./property-view-tree.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - MatTree, - MatTreeNode, - MatTreeNodeDef, - MatTreeNodePadding, - PropertyPreviewComponent, - PropertyEditorComponent, - MatIcon, - MatTooltip, - ], -}) -export class PropertyViewTreeComponent { - protected readonly supportedApis = inject(SUPPORTED_APIS); - private readonly signalGraph = inject(SignalGraphManager); - - readonly dataSource = input.required(); - readonly treeControl = input.required>(); - readonly updateValue = output(); - readonly inspect = output(); - readonly logValue = output(); - readonly showSignalGraph = output(); - - protected readonly signalGraphEnabled = () => this.supportedApis().signals; - - hasChild = (_: number, node: FlatNode): boolean => node.expandable; - - toggle(node: FlatNode): void { - if (this.treeControl().isExpanded(node)) { - this.treeControl().collapse(node); - return; - } - this.expand(node); - } - - expand(node: FlatNode): void { - const {prop} = node; - if (!prop.descriptor.expandable) { - return; - } - this.treeControl().expand(node); - } - - handleUpdate(node: FlatNode, newValue: unknown): void { - this.updateValue.emit({ - node, - newValue, - }); - } - - handleLogValue(event: Event, node: FlatNode): void { - event.stopPropagation(); - this.logValue.emit(node); - } - - getSignalNode(node: FlatNode): DebugSignalGraphNode | null { - if (node.prop.descriptor.containerType?.includes('Signal')) { - return this.signalGraph.graph()?.nodes.find((sn) => sn.label === node.prop.name) ?? null; - } - return null; - } - - showGraph(event: Event, node: DebugSignalGraphNode) { - event.stopPropagation(); - this.showSignalGraph.emit(node); - } -} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts index 30abbe1f5e16..da024244ef1e 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts @@ -9,10 +9,10 @@ import {ChangeDetectionStrategy, Component, computed, inject, input, output} from '@angular/core'; import {DebugSignalGraphNode, DirectivePosition} from '../../../../../../../protocol'; -import {ElementPropertyResolver, FlatNode} from '../../property-resolver/element-property-resolver'; +import {ElementPropertyResolver} from '../../property-resolver/element-property-resolver'; import {PropertyViewBodyComponent} from './property-view-body/property-view-body.component'; import {PropertyViewHeaderComponent} from './property-view-header/property-view-header.component'; -import {CdkAutofill} from '@angular/cdk/text-field'; +import {FlatNode} from '../../../../shared/object-tree-explorer/object-tree-types'; @Component({ selector: 'ng-property-view', diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.html index 5d69de8811ad..14ef7c9aafa2 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-details.component.html @@ -25,14 +25,14 @@
    @if (node.label) {
    Name:
    -
    {{node.label}}
    +
    {{ node.label }}
    }
    Type:
    - {{node.kind}} + {{ node.kind }}
    Epoch:
    -
    {{node.epoch}}
    +
    {{ node.epoch }}
    @if (node.kind === 'signal' || node.kind === 'computed') { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.html index 143f238337e3..1ccd0af75188 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signals-view/signals-details/signals-value-tree/signals-value-tree.component.html @@ -4,7 +4,7 @@ @if (node.level > 0) { {{ node.prop.name }} } - {{node.prop.descriptor.preview}} + {{ node.prop.descriptor.preview }}

    (source: Signal, defaultValue: T) templateUrl: './frame-selector.component.html', styleUrls: ['./frame-selector.component.scss'], styles: ` - :host { --max-bar-height: ${MAX_HEIGHT}px } + :host { + --max-bar-height: ${MAX_HEIGHT}px; + } `, imports: [ MatTooltip, diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/flamegraph-visualizer/flamegraph-visualizer.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/flamegraph-visualizer/flamegraph-visualizer.component.html index 57c4351ee5a2..4bc321eafad5 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/flamegraph-visualizer/flamegraph-visualizer.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/flamegraph-visualizer/flamegraph-visualizer.component.html @@ -1,6 +1,6 @@ diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/recording-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/recording-visualizer.component.ts index d3b987900224..d87f0aa87c71 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/recording-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/recording-visualizer/recording-visualizer.component.ts @@ -42,8 +42,18 @@ export class RecordingVisualizerComponent { readonly changeDetection = input.required(); readonly cmpVisualizationModes = VisualizationMode; - private readonly selectedNode = linkedSignal({ - source: this.visualizationMode, + + private readonly selectedNodeCleanUpDeps = computed( + // We don't care about the output format as long as + // the value is different when a dependency changes + // (i.e. it acts as a hash). + // NOTE: It's safe to stringify the frames since they + // are valid JSON objects that are also exported as part + // of the profiler results report. + () => JSON.stringify(this.frame()) + this.visualizationMode(), + ); + private readonly selectedNode = linkedSignal({ + source: this.selectedNodeCleanUpDeps, computation: () => null, }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/BUILD.bazel index f092fc471026..20cfee26614c 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/BUILD.bazel @@ -1,6 +1,6 @@ load("//devtools/tools:defaults.bzl", "ng_project", "ng_web_test_suite", "sass_binary", "ts_test_library") -package(default_visibility = ["//:__subpackages__"]) +package(default_visibility = ["//devtools:__subpackages__"]) sass_binary( name = "router_tree_styles", @@ -10,44 +10,31 @@ sass_binary( ], ) -sass_binary( - name = "router_details_row_styles", - src = "route-details-row.component.scss", -) - -sass_binary( - name = "route_data_tree_styles", - src = "route-data-tree/route-data-tree.component.scss", +ng_project( + name = "router-tree-fns", + srcs = ["router-tree-fns.ts"], deps = [ - "//devtools/projects/ng-devtools/src/styles:typography", + "//devtools/projects/ng-devtools/src/lib/shared/tree-visualizer", + "//devtools/projects/protocol", ], ) ng_project( name = "router-tree", srcs = [ - "route-data-tree/route-data-tree.component.ts", - "route-details-row.component.ts", "router-tree.component.ts", - "router-tree-fns.ts", ], angular_assets = [ ":router-tree.component.html", ":router_tree_styles", - ":route-details-row.component.html", - ":router_details_row_styles", - ":route-data-tree/route-data-tree.component.html", - ":route_data_tree_styles", ], deps = [ - "//:node_modules/@angular/common", + ":router-tree-fns", "//:node_modules/@angular/core", "//:node_modules/@angular/material", - "//:node_modules/@types/d3", - "//:node_modules/d3", - "//:node_modules/rxjs", "//devtools/projects/ng-devtools/src/lib/application-operations", "//devtools/projects/ng-devtools/src/lib/application-services:frame_manager", + "//devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row", "//devtools/projects/ng-devtools/src/lib/shared/button", "//devtools/projects/ng-devtools/src/lib/shared/split", "//devtools/projects/ng-devtools/src/lib/shared/tree-visualizer", @@ -59,7 +46,6 @@ ng_project( ts_test_library( name = "router_tree_test", srcs = [ - "route-details-row.component.spec.ts", "router-tree.component.spec.ts", "router-tree-fns.spec.ts", ], @@ -68,10 +54,8 @@ ts_test_library( ], deps = [ ":router-tree", - "//:node_modules/@angular/common", + ":router-tree-fns", "//:node_modules/@angular/core", - "//:node_modules/@angular/forms", - "//:node_modules/@angular/platform-browser", "//devtools/projects/ng-devtools/src/lib/application-operations", "//devtools/projects/ng-devtools/src/lib/application-services:frame_manager", "//devtools/projects/protocol", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.html deleted file mode 100644 index eb3bd8ff4eb7..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.html +++ /dev/null @@ -1,34 +0,0 @@ - - -
    - {{ node.name }}: - {{ node.preview }} - @if (showViewSourceButton()) { - - } -
    -
    - - - -
    diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.scss deleted file mode 100644 index 6532e7c674eb..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.scss +++ /dev/null @@ -1,82 +0,0 @@ -@use '../../../../styles/typography'; - -:host { - width: 100%; - display: block; - overflow: auto; - - .data-row { - $margin-left: 0.75rem; - $second-row-text-indent: 0.5rem; - - @extend %monospaced; - display: inline; - text-indent: -$second-row-text-indent; - margin-left: $margin-left + $second-row-text-indent; - - &.expandable-row { - position: relative; - border: none; - background: none; - padding: 0; - text-align: left; - - .expand-icon { - position: absolute; - font-size: 12px; - width: 12px; - height: 12px; - top: 0.175rem; - left: -0.875rem - $second-row-text-indent; - text-indent: 0; - cursor: pointer; - } - } - } - - mat-tree-node { - min-height: 1.375rem !important; - cursor: default; - } - - .name { - color: var(--color-tree-node-element-name); - } - - .value:not(:last-child) { - margin-right: 0.25rem; - } - - .data-action-btn { - position: relative; - border-radius: 50%; - flex: 0 0 16px; - width: 16px; - height: 16px; - border: none; - display: inline-flex; - align-items: center; - justify-content: center; - color: var(--quaternary-contrast); - background-color: transparent; - vertical-align: middle; - padding: 0; - top: -1px; - cursor: pointer; - margin-left: 0.25rem; - - &:hover { - $color: color-mix(in srgb, var(--dynamic-blue-02) 80%, var(--full-contrast) 20%); - background: $color; - outline: 2px solid $color; - color: var(--septenary-contrast); - } - - mat-icon { - width: 16px; - height: 16px; - font-size: 16px; - top: 0; - } - } -} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.ts deleted file mode 100644 index b6b29b649f09..000000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-data-tree/route-data-tree.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core'; -import {MatIconModule} from '@angular/material/icon'; -import {MatTooltipModule} from '@angular/material/tooltip'; -import {MatTreeModule} from '@angular/material/tree'; - -interface TreeNode { - name: string; - value: any; - preview: string; - isExpandable: boolean; - children: TreeNode[]; -} - -@Component({ - selector: 'ng-route-data-tree', - templateUrl: './route-data-tree.component.html', - styleUrls: ['./route-data-tree.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [MatTreeModule, MatIconModule, MatTooltipModule], -}) -export class RouteDataTreeComponent { - readonly data = input.required(); - - readonly showViewSourceButton = input(false); - - readonly viewSource = output(); - - protected readonly treeData = computed(() => this.buildTree(this.data())); - - protected readonly childrenAccessor = (node: TreeNode): TreeNode[] => node.children; - - private buildTree(data: any): TreeNode[] { - const isArray = Array.isArray(data); - const hasKeyValue = isArray && data?.[0]?.key !== undefined; - - if (isArray && hasKeyValue) { - const obj: Record = {}; - - for (const item of data) { - obj[item.key] = item.value; - } - - return this.buildObjectNodes(obj); - } - - return isArray ? this.buildArrayNodes(data) : this.buildObjectNodes(data); - } - - private isObject(value: any): boolean { - return value !== null && typeof value === 'object'; - } - - private buildArrayNodes(items: any[]): TreeNode[] { - return items.map((item, index) => this.createNode(`[${index}]`, item)); - } - - private buildObjectNodes(obj: Record): TreeNode[] { - return Object.keys(obj).map((key) => this.createNode(key, obj[key])); - } - - private createNode(name: string, value: any): TreeNode { - const isExpandable = this.isObject(value); - return { - name, - value, - preview: this.getValuePreview(value), - isExpandable, - children: isExpandable ? this.buildTree(value) : [], - }; - } - - protected hasChild = (_: number, node: TreeNode): boolean => node.isExpandable; - - protected handleViewSource(node: TreeNode): void { - this.viewSource.emit(node.value); - } - - private getValuePreview(value: any): string { - const type = typeof value; - - if (type === 'string') return value; - if (type === 'number' || type === 'boolean') return String(value); - if (Array.isArray(value)) return `[${value.length}]`; - if (type === 'object') return `{...}`; - - return String(value); - } -} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/BUILD.bazel new file mode 100644 index 000000000000..6baf8a6ecd80 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/BUILD.bazel @@ -0,0 +1,57 @@ +load("//devtools/tools:defaults.bzl", "ng_project", "ng_web_test_suite", "sass_binary", "ts_test_library") + +package(default_visibility = ["//devtools:__subpackages__"]) + +sass_binary( + name = "router_details_row_styles", + src = "route-details-row.component.scss", + deps = [ + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:prop-action-btn", + ], +) + +ng_project( + name = "router-details-row", + srcs = [ + "route-data-serializer.ts", + "route-details-row.component.ts", + ], + angular_assets = [ + ":route-details-row.component.html", + ":router_details_row_styles", + ], + deps = [ + "//:node_modules/@angular/common", + "//:node_modules/@angular/core", + "//:node_modules/@angular/material", + "//devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree:router-tree-fns", + "//devtools/projects/ng-devtools/src/lib/shared/button", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", + "//devtools/projects/protocol", + ], +) + +ts_test_library( + name = "router_details_row_test", + srcs = [ + "route-data-serializer.spec.ts", + "route-details-row.component.spec.ts", + ], + visibility = [ + "//visibility:private", + ], + deps = [ + ":router-details-row", + "//:node_modules/@angular/core", + "//:node_modules/@angular/platform-browser", + "//devtools/projects/protocol", + ], +) + +ng_web_test_suite( + name = "test", + deps = [ + ":router_details_row_test", + ], +) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.spec.ts new file mode 100644 index 000000000000..5a8b0d688ac9 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.spec.ts @@ -0,0 +1,255 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {buildRouteDataTree} from './route-data-serializer'; +import {PropType} from '../../../../../../protocol'; + +describe('route-data-serializer', () => { + describe('buildRouteDataTree', () => { + it('should serialize a flat object', () => { + const obj = { + foo: 'test', + bar: 314, + baz: null, + qux: undefined, + quux: Symbol('QUUX'), + quuux: BigInt(9007199254740991), + }; + + const flatNode = buildRouteDataTree(obj); + + expect(flatNode).toEqual([ + { + expandable: false, + level: 0, + prop: { + name: 'foo', + parent: null, + descriptor: { + preview: 'test', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 0, + prop: { + name: 'bar', + parent: null, + descriptor: { + preview: '314', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 0, + prop: { + name: 'baz', + parent: null, + descriptor: { + preview: 'null', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 0, + prop: { + name: 'qux', + parent: null, + descriptor: { + preview: 'undefined', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 0, + prop: { + name: 'quux', + parent: null, + descriptor: { + preview: 'Symbol(QUUX)', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 0, + prop: { + name: 'quuux', + parent: null, + descriptor: { + preview: '9007199254740991n', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + ]); + }); + + it('should serialize a nested object', () => { + const obj = { + foo: 'bar', + baz: { + qux: 314, + quux: [3, 1, 4], + }, + }; + + const flatNode = buildRouteDataTree(obj); + + expect(flatNode).toEqual([ + { + expandable: false, + level: 0, + prop: { + name: 'foo', + parent: null, + descriptor: { + preview: 'bar', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: true, + level: 0, + prop: { + name: 'baz', + parent: null, + descriptor: { + preview: '{...}', + expandable: true, + editable: false, + containerType: null, + type: PropType.Unknown, + value: [ + { + expandable: false, + level: 1, + prop: { + name: 'qux', + parent: null, + descriptor: { + preview: '314', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: true, + level: 1, + prop: { + name: 'quux', + parent: null, + descriptor: { + preview: 'Array(3)', + expandable: true, + editable: false, + containerType: null, + type: PropType.Unknown, + value: [ + { + expandable: false, + level: 2, + prop: { + name: '0', + parent: null, + descriptor: { + preview: '3', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 2, + prop: { + name: '1', + parent: null, + descriptor: { + preview: '1', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + { + expandable: false, + level: 2, + prop: { + name: '2', + parent: null, + descriptor: { + preview: '4', + value: [], + expandable: false, + editable: false, + containerType: null, + type: PropType.Unknown, + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ]); + }); + }); +}); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.ts new file mode 100644 index 000000000000..d7f055365fd8 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-data-serializer.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {PropType} from '../../../../../../protocol'; +import {FlatNode} from '../../../shared/object-tree-explorer/object-tree-types'; + +export function buildRouteDataTree(data: unknown, level = 0): FlatNode[] { + return Array.isArray(data) + ? buildArrayNodes(data, level) + : buildObjectNodes(data as Record, level); +} + +function buildArrayNodes(items: unknown[], level: number): FlatNode[] { + return items.map((item, index) => createNode(index.toString(), item, level)); +} + +function buildObjectNodes(obj: Record, level: number): FlatNode[] { + return Object.keys(obj).map((key) => createNode(key, obj[key], level)); +} + +function createNode(name: string, value: unknown, level: number): FlatNode { + const expandable = value !== null && typeof value === 'object'; + + return { + expandable, + level, + prop: { + name, + parent: null, // Placeholder. Not required. + descriptor: { + preview: getValuePreview(value), + // Values are represented by an array of FlatNodes. + // Therefore, it's empty when it's non-expandable. + value: expandable ? buildRouteDataTree(value, level + 1) : [], + expandable, + editable: false, + containerType: null, + type: PropType.Unknown, // Placeholder. Not required. + }, + }, + }; +} + +function getValuePreview(value: unknown): string { + switch (typeof value) { + case 'string': + return value as string; + + case 'number': + case 'boolean': + case 'symbol': + return String(value); + + case 'bigint': + return `${String(value)}n`; + + case 'undefined': + return 'undefined'; + + case 'object': { + if (value === null) return 'null'; + if (Array.isArray(value)) return `Array(${value.length})`; + + return '{...}'; + } + } + + return String(value); +} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.html similarity index 65% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.html rename to devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.html index 50d9540f9fd7..665c94c609c6 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.html @@ -1,4 +1,5 @@ -{{label()}} + +{{ label() }} @switch (type()) { @case ('flag') { @@ -12,24 +13,33 @@ {{ provider || '[empty string] ' }}
    } } @default { @if (rowValue() && renderValueAsJson()) { - + + + @if (!node.expandable) { + + } + + } @else {
    {{ rowValue() || '[empty string] ' }}
    } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.scss similarity index 86% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.scss rename to devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.scss index ab7f43acd219..6ba52f9513f4 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.scss +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.scss @@ -1,3 +1,5 @@ +@use '../../../shared/object-tree-explorer/prop-action-btn'; + .tag-active, .tag-inactive { border-radius: 1rem; @@ -47,3 +49,7 @@ button { td { width: 100%; } + +ng-object-tree-explorer { + --ote-padding: 0rem; +} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.spec.ts similarity index 100% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.spec.ts rename to devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.spec.ts diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.ts similarity index 59% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.ts rename to devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.ts index 3f607dd04c2a..38b2c9b064f9 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/route-details-row.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-details-row/route-details-row.component.ts @@ -9,10 +9,13 @@ import {Component, computed, input, output, ChangeDetectionStrategy} from '@angular/core'; import {NgTemplateOutlet} from '@angular/common'; import {MatIcon} from '@angular/material/icon'; -import {ButtonComponent} from '../../shared/button/button.component'; -import {RouterTreeNode} from './router-tree-fns'; import {MatTooltip} from '@angular/material/tooltip'; -import {RouteDataTreeComponent} from './route-data-tree/route-data-tree.component'; + +import {RouterTreeNode} from '../router-tree-fns'; +import {ButtonComponent} from '../../../shared/button/button.component'; +import {ObjectTreeExplorerComponent} from '../../../shared/object-tree-explorer/object-tree-explorer.component'; +import {buildRouteDataTree} from './route-data-serializer'; +import {FlatNode} from '../../../shared/object-tree-explorer/object-tree-types'; export type RowType = 'text' | 'flag' | 'list'; export type ActionBtnType = 'none' | 'view-source' | 'navigate'; @@ -21,7 +24,7 @@ export type ActionBtnType = 'none' | 'view-source' | 'navigate'; selector: '[ng-route-details-row]', templateUrl: './route-details-row.component.html', styleUrls: ['./route-details-row.component.scss'], - imports: [NgTemplateOutlet, ButtonComponent, MatIcon, MatTooltip, RouteDataTreeComponent], + imports: [NgTemplateOutlet, ButtonComponent, MatIcon, MatTooltip, ObjectTreeExplorerComponent], changeDetection: ChangeDetectionStrategy.OnPush, }) export class RouteDetailsRowComponent { @@ -41,10 +44,23 @@ export class RouteDetailsRowComponent { }); readonly dataArray = computed(() => { - if (!this.data() || !this.dataKey()) { - return []; + const rowValue = this.rowValue(); + if (Array.isArray(rowValue)) { + return rowValue; } + return []; + }); - return this.rowValue(); + // Representation data for object-tree-visualizer + readonly treeData = computed(() => { + const rowValue = this.rowValue(); + if (rowValue && this.renderValueAsJson()) { + // Wrap the data in an object in order to render it as: > {...} + const value = typeof rowValue === 'object' ? {_root: rowValue} : rowValue; + return buildRouteDataTree(value); + } + return []; }); + + readonly treeDataChildrenAccessor = (node: FlatNode): FlatNode[] => node.prop.descriptor.value; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.html index be7089a116d4..9d21a1a0d413 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.html @@ -97,7 +97,7 @@

    Routes Details

    @if (data.pathMatch) { } - @if (route?.data?.data?.length > 0) { + @if (data.data) { Routes Details [data]="data" > } - @if (data.resolvers && data.resolvers.length > 0) { + @if (data.resolvers) { Routes Details [data]="data" actionBtnTooltip="View source" [actionBtnType]="hasStaticOptionRunGuardsAndResolvers() ? 'none' : 'view-source'" - (actionBtnClick)="viewFunctionSource(data.runGuardsAndResolvers, 'runGuardsAndResolvers')" + (actionBtnClick)=" + viewFunctionSource(data.runGuardsAndResolvers, 'runGuardsAndResolvers') + " > } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts index 7af14ba4f261..37fe30c15bea 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts @@ -22,7 +22,7 @@ import {TreeVisualizerComponent} from '../../shared/tree-visualizer/tree-visuali import {MatIconModule} from '@angular/material/icon'; import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar'; import {ApplicationOperations} from '../../application-operations/index'; -import {RouteDetailsRowComponent} from './route-details-row.component'; +import {RouteDetailsRowComponent} from './router-details-row/route-details-row.component'; import {FrameManager} from '../../application-services/frame_manager'; import {Events, MessageBus, Route, RunGuardsAndResolvers} from '../../../../../protocol'; import {SvgD3Node, TreeVisualizerConfig} from '../../shared/tree-visualizer/tree-visualizer'; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/transfer-state/transfer-state.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/transfer-state/transfer-state.component.html index ca5245fae4e9..dc527ecc222b 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/transfer-state/transfer-state.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/transfer-state/transfer-state.component.html @@ -124,7 +124,7 @@

    Transfer State is Empty

    - +
    diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.html b/devtools/projects/ng-devtools/src/lib/devtools.component.html index d1a95bf1221d..b129a02d94bc 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.html @@ -1,4 +1,4 @@ -
    +
    @switch (angularStatus()) { @case (AngularStatus.EXISTS) { @if (angularIsInDevMode()) { @@ -11,71 +11,83 @@ >
    } @else { -

    Angular Devtools only supports Angular versions 12 and above

    +
    + upgrade +

    + Angular DevTools only supports Angular versions {{ LAST_SUPPORTED_VERSION }} and + above. +

    +
    } } @else { -

    - We detected an application built with production configuration. Angular DevTools only - supports development builds. -

    - -
    -

    - If this application was built in development mode, please check if the - window.ng global object is available in your application. If it is missing, - then something is preventing Angular from running in development mode properly. +

    + code_off +

    + We detected an application built with a production configuration. Angular DevTools only + supports development builds.

    -
      -
    • - Are you calling enableProdMode() in your application? Read more about - enableProdMode() - on angular.dev. -
    • -
    • - Is "optimization": true set in your angular.json? Read more about - optimization configuration - on angular.dev. -
    • -
    • - Is "defaultConfiguration": "production" set in your angular.json? Read - more about + +
      +

      + If this application was built in development mode, please check if the + window.ng global object is available in your application. If it is + missing, then something is preventing Angular from running in development mode + properly. +

      +
        +
      • + Are you calling enableProdMode() in your application? Read more about + enableProdMode() + on angular.dev. +
      • +
      • + Is "optimization": true set in your angular.json? Read more about + optimization configuration + on angular.dev. +
      • +
      • + Is "defaultConfiguration": "production" set in your angular.json? Read + more about + default configurations + on angular.dev. +
      • +
      +

      + If you are still experiencing problems, you can open an issue with a reproduction on + our default configurations - on angular.dev. -

    • -
    -

    - If you are still experiencing problems, you can open an issue with a reproduction on our - issue tracker. -

    + href="https://github.com/angular/angular/issues/new?assignees=&labels=&projects=&template=4-devtools.yaml" + >issue tracker. +

    +
    } } @case (AngularStatus.DOES_NOT_EXIST) { -

    - + error +

    i - Angular application not detected. -

    + Angular application not detected. +

    +
    } @case (AngularStatus.UNKNOWN) {
    diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.scss b/devtools/projects/ng-devtools/src/lib/devtools.component.scss index 3ddecdf38580..cae166f1980c 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.scss +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.scss @@ -42,21 +42,30 @@ } } -.text-message { - @extend %heading-500; - text-align: center; - cursor: help; +.devtools-state-screen { + padding: 1rem; + height: 100%; + overflow-y: auto; + box-sizing: border-box; - .info-icon { - display: inline-block; - font-size: 0.8em; - border-radius: 50%; - border: solid 2px var(--primary-contrast); - cursor: pointer; - width: 16px; - height: 16px; - font-weight: bold; + mat-icon { + display: block; + font-size: 32px; + width: 32px; + height: 32px; + margin-inline: auto; + margin-block-start: 0.5rem; + color: var(--quinary-contrast); + } + + .text-message { + @extend %heading-500; text-align: center; + margin: 0; + + &[matTooltip] { + cursor: help; + } } } @@ -67,6 +76,15 @@ margin: auto; border: 1px solid var(--quinary-contrast); border-radius: 16px; + margin-top: 1rem; + + p:first-of-type { + margin-top: 0; + } + + p:last-of-type { + margin-bottom: 0; + } code { padding: 2px; diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.ts b/devtools/projects/ng-devtools/src/lib/devtools.component.ts index 61e35f263dfb..4c3a0191a1e2 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.ts @@ -24,7 +24,7 @@ import {DevToolsTabsComponent} from './devtools-tabs/devtools-tabs.component'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {Frame} from './application-environment'; import {BrowserStylesService} from './application-services/browser_styles_service'; -import {MatIconRegistry} from '@angular/material/icon'; +import {MatIcon, MatIconRegistry} from '@angular/material/icon'; import {SUPPORTED_APIS} from './application-providers/supported_apis'; const DETECT_ANGULAR_ATTEMPTS = 20; @@ -47,13 +47,13 @@ enum AngularStatus { EXISTS, } -const LAST_SUPPORTED_VERSION = 9; +export const LAST_SUPPORTED_VERSION = 12; @Component({ selector: 'ng-devtools', templateUrl: './devtools.component.html', styleUrls: ['./devtools.component.scss'], - imports: [DevToolsTabsComponent, MatTooltip, MatProgressSpinnerModule, MatTooltipModule], + imports: [DevToolsTabsComponent, MatIcon, MatTooltip, MatProgressSpinnerModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, }) export class DevToolsComponent implements OnDestroy { @@ -66,6 +66,8 @@ export class DevToolsComponent implements OnDestroy { readonly hydration = signal(false); readonly ivy = signal(undefined); + readonly LAST_SUPPORTED_VERSION = LAST_SUPPORTED_VERSION; + readonly supportedVersion = computed(() => { const version = this.angularVersion(); if (!version) { @@ -100,7 +102,10 @@ export class DevToolsComponent implements OnDestroy { this.ivy.set(ivy); this._interval$.unsubscribe(); this.hydration.set(hydration); - this.supportedApis.init(supportedApis); + + if (supportedApis) { + this.supportedApis.init(supportedApis); + } }); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools_spec.ts b/devtools/projects/ng-devtools/src/lib/devtools_spec.ts index bbd2dd517c2a..4671c4b041bc 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools_spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools_spec.ts @@ -9,7 +9,7 @@ import {Component} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {FrameManager} from './application-services/frame_manager'; -import {DevToolsComponent} from './devtools.component'; +import {DevToolsComponent, LAST_SUPPORTED_VERSION} from './devtools.component'; import {DevToolsTabsComponent} from './devtools-tabs/devtools-tabs.component'; import {MessageBus} from '../../../protocol'; import {SETTINGS_MOCK} from './application-services/test-utils/settings_mock'; @@ -54,8 +54,8 @@ describe('DevtoolsComponent', () => { component.angularStatus.set(component.AngularStatus.EXISTS); component.angularIsInDevMode.set(false); fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('.devtools').textContent).toContain( - 'We detected an application built with production configuration. Angular DevTools only supports development builds.', + expect(fixture.nativeElement.querySelector('.devtools-state-screen').textContent).toContain( + 'We detected an application built with a production configuration. Angular DevTools only supports development builds.', ); }); @@ -64,8 +64,8 @@ describe('DevtoolsComponent', () => { component.angularIsInDevMode.set(true); component.angularVersion.set('1.0.0'); fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('.devtools').textContent).toContain( - 'Angular Devtools only supports Angular versions 12 and above', + expect(fixture.nativeElement.querySelector('.devtools-state-screen').textContent).toContain( + `Angular DevTools only supports Angular versions ${LAST_SUPPORTED_VERSION} and above`, ); }); @@ -73,7 +73,7 @@ describe('DevtoolsComponent', () => { component.angularStatus.set(component.AngularStatus.DOES_NOT_EXIST); fixture.detectChanges(); // expect the text to be "Angular application not detected" - expect(fixture.nativeElement.querySelector('.not-detected').textContent).toContain( + expect(fixture.nativeElement.querySelector('.devtools-state-screen').textContent).toContain( 'Angular application not detected', ); }); diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/BUILD.bazel new file mode 100644 index 000000000000..855072383fba --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/BUILD.bazel @@ -0,0 +1,62 @@ +load("//devtools/tools:defaults.bzl", "ng_project", "ng_web_test_suite", "sass_binary", "sass_library", "ts_test_library") + +package(default_visibility = ["//devtools:__subpackages__"]) + +ng_project( + name = "types", + srcs = ["object-tree-types.ts"], + deps = [ + "//devtools/projects/protocol", + ], +) + +sass_binary( + name = "object-tree-explorer_styles", + src = "object-tree-explorer.component.scss", + deps = [ + "//devtools/projects/ng-devtools/src/styles:typography", + ], +) + +ng_project( + name = "object-tree-explorer", + srcs = ["object-tree-explorer.component.ts"], + angular_assets = [ + "object-tree-explorer.component.html", + ":object-tree-explorer_styles", + ], + deps = [ + ":types", + "//:node_modules/@angular/cdk", + "//:node_modules/@angular/common", + "//:node_modules/@angular/core", + "//:node_modules/@angular/material", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview", + ], +) + +sass_library( + name = "prop-action-btn", + srcs = ["_prop-action-btn.scss"], +) + +ts_test_library( + name = "object-tree-explorer_test", + srcs = ["object-tree-explorer.spec.ts"], + deps = [ + ":object-tree-explorer", + ":types", + "//:node_modules/@angular/core", + "//:node_modules/@angular/material", + "//:node_modules/@angular/platform-browser", + "//devtools/projects/protocol", + ], +) + +ng_web_test_suite( + name = "test", + deps = [ + ":object-tree-explorer_test", + ], +) diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/_prop-action-btn.scss b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/_prop-action-btn.scss new file mode 100644 index 000000000000..f10d30879c53 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/_prop-action-btn.scss @@ -0,0 +1,35 @@ +.prop-action-btn { + position: relative; + border-radius: 50%; + flex: 0 0 16px; + width: 16px; + height: 16px; + border: none; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--quaternary-contrast); + background-color: transparent; + vertical-align: middle; + padding: 0; + top: -1px; + cursor: pointer; + + &:not(:last-child) { + margin-right: 0.25rem; + } + + &:hover { + $color: color-mix(in srgb, var(--dynamic-blue-02) 80%, var(--full-contrast) 20%); + background: $color; + outline: 2px solid $color; + color: var(--septenary-contrast); + } + + mat-icon { + width: 16px; + height: 16px; + font-size: 16px; + top: 0; + } +} diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.html b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.html new file mode 100644 index 000000000000..057a668ea626 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.html @@ -0,0 +1,58 @@ + + +
    + @if (!omitRootNodesNames() || node.level > 0) { + {{ node.prop.name }}: + } + @if (!node.prop.descriptor.editable || !editingEnabled()) { + + } @else { + + } + @if (contentTemplateRef(); as contentTemplateRef) { + + } +
    +
    + + + +
    diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.scss b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.scss new file mode 100644 index 000000000000..71c7249eb216 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.scss @@ -0,0 +1,55 @@ +@use '../../../styles/typography'; + +:host { + --padding-internal: var(--ote-padding, 0.5rem); + --row-indent-internal: var(--ote-row-indent, 0rem); + width: 100%; + display: block; + overflow: auto; + + .property-list { + margin: var(--padding-internal); + } + + .prop-row { + --wrap-indent-internal: 1rem; + --margin-left-internal: calc(var(--row-indent-internal) + 0.875rem); + + @extend %monospaced; + display: inline; + text-indent: calc(-1 * var(--wrap-indent-internal)); + margin-left: calc(var(--margin-left-internal) + var(--wrap-indent-internal)); + + &.expandable-node { + position: relative; + border: none; + background: none; + padding: 0; + text-align: left; + + .expand-icon { + position: absolute; + font-size: 12px; + width: 12px; + height: 12px; + top: 0.175rem; + left: calc(-0.875rem - var(--wrap-indent-internal)); + text-indent: 0; + cursor: pointer; + } + } + } + + mat-tree-node { + min-height: 1.375rem !important; + cursor: default; + } + + .name { + color: var(--color-tree-node-element-name); + } + + .value:not(:last-child) { + margin-right: 0.25rem; + } +} diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.ts b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.ts new file mode 100644 index 000000000000..18ef8e495295 --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.component.ts @@ -0,0 +1,102 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { + ChangeDetectionStrategy, + Component, + contentChild, + effect, + input, + output, + TemplateRef, +} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import {DataSource} from '@angular/cdk/collections'; +import {FlatTreeControl} from '@angular/cdk/tree'; +import {MatIcon} from '@angular/material/icon'; +import { + MatTree, + MatTreeNode, + MatTreeNodeDef, + MatTreeNodePadding, + MatTreeNodeToggle, +} from '@angular/material/tree'; + +import {FlatNode} from './object-tree-types'; +import {PropertyPreviewComponent} from './property-preview/property-preview.component'; +import {PropertyEditorComponent} from './property-editor/property-editor.component'; + +const CHILDREN_INDENT = 14; // px + +/** + * Renders an expandable object tree. + * Accepts mat-tree `treeControl` or `childrenAccessor`, + * along with a mandatory `dataSource`. + * + * Usage: + * + * ``` + * + * + * + * + * + * + * + * Styling: + * - --ote-padding (CSS var) - container padding in `rem` (default: 0.5rem) + * - --ote-row-indent (CSS var) - row indent/left margin in `rem` (default: 0rem) + * - childrenIndent (input) - children indent in `px` (default: 14px) + * + */ +@Component({ + selector: 'ng-object-tree-explorer', + templateUrl: './object-tree-explorer.component.html', + styleUrl: './object-tree-explorer.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgTemplateOutlet, + MatTree, + MatTreeNode, + MatTreeNodeDef, + MatTreeNodePadding, + MatTreeNodeToggle, + MatIcon, + PropertyPreviewComponent, + PropertyEditorComponent, + ], +}) +export class ObjectTreeExplorerComponent { + protected readonly contentTemplateRef = contentChild(TemplateRef); + + protected readonly dataSource = input.required | FlatNode[]>(); + protected readonly treeControl = input | undefined>(); + protected readonly childrenAccessor = input<((datum: FlatNode) => FlatNode[]) | undefined>(); + protected readonly editingEnabled = input(false); + protected readonly omitRootNodesNames = input(false); + protected readonly childrenIndent = input(CHILDREN_INDENT); + + protected readonly propInspect = output(); + protected readonly propValueUpdate = output<{ + node: FlatNode; + newValue: unknown; + }>(); + + hasChild = (_: number, node: FlatNode): boolean => node.expandable; + + constructor() { + effect(() => { + if (!this.treeControl() && !this.childrenAccessor()) { + throw new Error('The object tree explorer requires a "treeControl" or "childrenAccessor"'); + } + }); + } +} diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.spec.ts b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.spec.ts new file mode 100644 index 000000000000..3fc80d75ce2b --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-explorer.spec.ts @@ -0,0 +1,147 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {MatTreeNode} from '@angular/material/tree'; +import {By} from '@angular/platform-browser'; + +import {ObjectTreeExplorerComponent} from './object-tree-explorer.component'; +import {FlatNode} from './object-tree-types'; +import {PropType} from '../../../../../protocol'; + +const mockDataSource: FlatNode[] = [ + { + level: 0, + expandable: false, + prop: { + name: 'foo', + parent: null, + descriptor: { + editable: false, + containerType: null, + type: PropType.Unknown, + expandable: false, + preview: 'string_val', + value: [], + }, + }, + }, + { + level: 0, + expandable: false, + prop: { + name: 'bar', + parent: null, + descriptor: { + editable: true, + containerType: null, + type: PropType.Unknown, + expandable: false, + preview: 'editable_string_val', + value: [], + }, + }, + }, + { + level: 1, + expandable: true, + prop: { + name: 'baz', + parent: null, + descriptor: { + editable: false, + containerType: null, + type: PropType.Unknown, + expandable: true, + preview: 'editable_string_val', + value: [ + { + level: 1, + expandable: false, + prop: { + name: 'qux', + parent: null, + descriptor: { + editable: false, + containerType: null, + type: PropType.Unknown, + expandable: false, + preview: 'false', + value: [], + }, + }, + }, + ], + }, + }, + }, +]; + +describe('ObjectTreeExplorerComponent', () => { + let component: ObjectTreeExplorerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ObjectTreeExplorerComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ObjectTreeExplorerComponent); + component = fixture.componentInstance; + fixture.componentRef.setInput('dataSource', mockDataSource); + fixture.componentRef.setInput('childrenAccessor', (fl: FlatNode) => fl.prop.descriptor.value); + fixture.componentRef.setInput('editingEnabled', true); + + fixture.detectChanges(); + }); + + it('should throw an error if treeControl and childrenAccessor are missing', () => { + expect(() => { + fixture.componentRef.setInput('dataSource', mockDataSource); + fixture.componentRef.setInput('childrenAccessor', null); + fixture.componentRef.setInput('treeControl', null); + fixture.detectChanges(); + }).toThrowError('The object tree explorer requires a "treeControl" or "childrenAccessor"'); + }); + + it('should render property preview if the property is NOT editable', () => { + const nodes = fixture.debugElement.queryAll(By.directive(MatTreeNode)); + const firstNode = nodes[0]; + const propPreview = firstNode.query(By.css('.non-expandable-node ng-property-preview')); + + expect(propPreview).toBeTruthy(); + }); + + it('should enable the property editor if the property is editable', () => { + const nodes = fixture.debugElement.queryAll(By.directive(MatTreeNode)); + const secondNode = nodes[1]; + const propEditor = secondNode.query(By.css('.non-expandable-node ng-property-editor')); + + expect(propEditor).toBeTruthy(); + }); + + it('should render an expandable node if the node is expandable', () => { + const nodes = fixture.debugElement.queryAll(By.directive(MatTreeNode)); + const thirdNode = nodes[2]; + const expandableWrapper = thirdNode.query(By.css('.expandable-node')); + + expect(expandableWrapper).toBeTruthy(); + }); + + it('should NOT render root nodes names when omitRootNodesNames is provided', async () => { + fixture.componentRef.setInput('omitRootNodesNames', true); + fixture.detectChanges(); + + const nodes = fixture.debugElement.queryAll(By.directive(MatTreeNode)); + + for (const node of nodes) { + const name = node.query(By.css('.node')); + expect(name).toBeFalsy(); + } + }); +}); diff --git a/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-types.ts b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-types.ts new file mode 100644 index 000000000000..4660ccb542cb --- /dev/null +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/object-tree-types.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Descriptor} from '../../../../../protocol'; + +/** + * An object-tree-exlorer data source unit that represents an object. + */ +export interface FlatNode { + /** + * Determines whether the node/property is expandable (e.g. an object or an array). + */ + expandable: boolean; + + /** + * Property that the flat node represents. + */ + prop: Property; + + /** + * Depth of the node/property relative to the root of the object tree. + */ + level: number; +} + +/** + * Flat node property data. + */ +export interface Property { + /** + * Name of the property. + */ + name: string; + + /** + * The descriptor of the property containing the property details. + */ + descriptor: Descriptor; + + /** + * Parent of the property, if there is such. + */ + parent: Property | null; +} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/BUILD.bazel similarity index 88% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/BUILD.bazel rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/BUILD.bazel index 1c5bc60df837..cb0783b7acad 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/BUILD.bazel @@ -22,6 +22,6 @@ ng_project( deps = [ "//:node_modules/@angular/core", "//:node_modules/@angular/forms", - "//devtools/projects/protocol", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.html b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.html similarity index 77% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.html rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.html index 7b1d6b6349be..58d65455fe90 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.html +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.html @@ -1,6 +1,6 @@ @switch (currentPropertyState()) { @case (readState) { - {{ previewValue() }} + {{ property().descriptor.preview }} } @case (writeState) {
    @@ -16,7 +16,7 @@ (blur)="onBlur()" placeholder="Property value" /> - {{valueToSubmit()}} + {{ valueToSubmit() }}
    } } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.scss b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.scss similarity index 92% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.scss rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.scss index c83a446c30a3..9dc95924a0be 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.scss +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.scss @@ -1,4 +1,4 @@ -@use '../../../../../../../../styles/typography'; +@use '../../../../styles/typography'; :host { cursor: text; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.ts b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.ts similarity index 83% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.ts rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.ts index babb5d839928..c48b4896dd64 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-editor/property-editor.component.ts +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-editor/property-editor.component.ts @@ -7,7 +7,6 @@ */ import { - afterNextRender, Component, ElementRef, effect, @@ -17,9 +16,10 @@ import { viewChild, ChangeDetectionStrategy, linkedSignal, + computed, } from '@angular/core'; import {FormsModule} from '@angular/forms'; -import {ContainerType} from '../../../../../../../../../../protocol'; +import {Property} from '../object-tree-types'; type EditorType = string | number | boolean; type EditorResult = EditorType | Array; @@ -48,15 +48,12 @@ const parseValue = (value: EditorResult): EditorResult => { }, }) export class PropertyEditorComponent { - readonly key = input.required(); - readonly initialValue = input.required(); - readonly previewValue = input.required(); + protected readonly inputEl = viewChild>('inputEl'); - readonly containerType = input(); + protected readonly property = input.required(); + protected readonly initialValue = computed(() => this.property().descriptor.value); - readonly updateValue = output(); - - readonly inputEl = viewChild>('inputEl'); + protected readonly updateValue = output(); readState = PropertyEditorState.Read; writeState = PropertyEditorState.Write; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/BUILD.bazel similarity index 86% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/BUILD.bazel rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/BUILD.bazel index 9a83f38e2204..5aca1fe20665 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/BUILD.bazel @@ -21,7 +21,7 @@ ng_project( ], deps = [ "//:node_modules/@angular/core", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", + "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types", "//devtools/projects/protocol", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.html b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.html similarity index 100% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.html rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.html diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.scss b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.scss similarity index 100% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.scss rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.scss diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.ts b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.ts similarity index 84% rename from devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.ts rename to devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.ts index 2c5d9ccbc09c..71a06dd65c09 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body/property-view-tree/property-preview/property-preview.component.ts +++ b/devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer/property-preview/property-preview.component.ts @@ -7,9 +7,8 @@ */ import {ChangeDetectionStrategy, Component, computed, input, output} from '@angular/core'; -import {PropType} from '../../../../../../../../../../protocol'; - -import {FlatNode} from '../../../../../property-resolver/element-property-resolver'; +import {PropType} from '../../../../../../protocol'; +import {FlatNode} from '../object-tree-types'; @Component({ selector: 'ng-property-preview', diff --git a/devtools/projects/ng-devtools/src/lib/shared/split/split.component.ts b/devtools/projects/ng-devtools/src/lib/shared/split/split.component.ts index f15d1b7e7a18..9bde7e26cb4e 100644 --- a/devtools/projects/ng-devtools/src/lib/shared/split/split.component.ts +++ b/devtools/projects/ng-devtools/src/lib/shared/split/split.component.ts @@ -101,9 +101,9 @@ import { (mouseup)="clickGutter($event, $index + 1)" (touchend)="clickGutter($event, $index + 1)" > - @if(showGutterIcon()) { -
    - } + @if (showGutterIcon()) { +
    + }
    } }`, diff --git a/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts index 2943a95e824f..ffac5a992484 100644 --- a/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/shared/tree-visualizer/tree-visualizer.component.ts @@ -56,19 +56,23 @@ export class TreeVisualizerComponent { protected readonly nodeMouseover = output>(); readonly panning = signal(false); + private readonly visualizer = signal | null>(null); private initialRender: boolean = true; - private visualizer?: TreeVisualizer; constructor() { - afterNextRender(() => { - this.visualizer?.cleanup(); - this.visualizer = new TreeVisualizer( - this.container().nativeElement, - this.group().nativeElement, - this.config(), - ); - this.ready.emit(); + afterNextRender({ + write: () => this.visualizer()?.cleanup(), // Cleans up the visualization DOM + read: () => { + this.visualizer.set( + new TreeVisualizer( + this.container().nativeElement, + this.group().nativeElement, + this.config(), + ), + ); + this.ready.emit(); + }, }); effect(() => { @@ -76,7 +80,7 @@ export class TreeVisualizerComponent { }); inject(DestroyRef).onDestroy(() => { - this.visualizer?.dispose(); + this.visualizer()?.dispose(); }); } @@ -85,26 +89,27 @@ export class TreeVisualizerComponent { } snapToRoot(scale?: number) { - this.visualizer?.snapToRoot(scale); + this.visualizer()?.snapToRoot(scale); } snapToNode(node: T, scale?: number) { - this.visualizer?.snapToNode(node, scale); + this.visualizer()?.snapToNode(node, scale); } getNodeById(id: string) { - return this.visualizer?.getInternalNodeById(id); + return this.visualizer()?.getInternalNodeById(id); } private renderGraph(root: T): void { - if (!this.visualizer) { + const visualizer = this.visualizer(); + if (!visualizer) { return; } - this.visualizer.render(root); - this.visualizer.onNodeClick((_, node) => this.nodeClick.emit(node)); - this.visualizer.onNodeMouseout((_, node) => this.nodeMouseout.emit(node)); - this.visualizer.onNodeMouseover((_, node) => this.nodeMouseover.emit(node)); + visualizer.render(root); + visualizer.onNodeClick((_, node) => this.nodeClick.emit(node)); + visualizer.onNodeMouseout((_, node) => this.nodeMouseout.emit(node)); + visualizer.onNodeMouseover((_, node) => this.nodeMouseover.emit(node)); this.render.emit({initial: this.initialRender}); if (this.initialRender) { diff --git a/devtools/projects/ng-devtools/src/styles/_typography.scss b/devtools/projects/ng-devtools/src/styles/_typography.scss index c79bea63b9cf..5715c2a07430 100644 --- a/devtools/projects/ng-devtools/src/styles/_typography.scss +++ b/devtools/projects/ng-devtools/src/styles/_typography.scss @@ -17,7 +17,7 @@ $font-size: 0.75rem; // 12px font-family: $font-family; font-size: 1.125rem; // 18px font-weight: 500; - line-height: 1rem; + line-height: 1.375rem; letter-spacing: normal; padding: 0.5rem 0.25rem; } @@ -26,7 +26,7 @@ $font-size: 0.75rem; // 12px font-family: $font-family; font-size: 1rem; // 16px font-weight: 500; - line-height: 1rem; + line-height: 1.375rem; letter-spacing: normal; padding: 0.5rem 0.25rem; } diff --git a/devtools/projects/protocol/src/lib/messages.ts b/devtools/projects/protocol/src/lib/messages.ts index 82be36e4462c..9a5baa86b96a 100644 --- a/devtools/projects/protocol/src/lib/messages.ts +++ b/devtools/projects/protocol/src/lib/messages.ts @@ -322,8 +322,8 @@ export interface Route { providers?: string[]; title?: string; children?: Array; - data?: any; - resolvers?: any; + data?: {[key: string | symbol]: any}; + resolvers?: {[key: string]: string}; path: string; component: string; redirectTo?: string; @@ -385,7 +385,7 @@ export interface Events { devMode: boolean; ivy: boolean; hydration: boolean; - supportedApis: SupportedApis; + supportedApis: SupportedApis | null; }) => void; inspectorStart: () => void; @@ -447,6 +447,7 @@ export interface Events { enableFrameConnection: (frameId: number, tabId: number) => void; frameConnected: (frameId: number) => void; detectAngular: (detectionResult: AngularDetection) => void; + backendInstalled: () => void; backendReady: () => void; log: (logEvent: {message: string; level: 'log' | 'warn' | 'debug' | 'error'}) => void; diff --git a/devtools/projects/shell-browser/src/app/content-script.ts b/devtools/projects/shell-browser/src/app/content-script.ts index 03252aca6456..a0bec5fcd644 100644 --- a/devtools/projects/shell-browser/src/app/content-script.ts +++ b/devtools/projects/shell-browser/src/app/content-script.ts @@ -13,7 +13,6 @@ import {BACKEND_URI, CONTENT_SCRIPT_URI, DETECT_ANGULAR_SCRIPT_URI} from './comm import {SamePageMessageBus} from './same-page-message-bus'; let backgroundDisconnected = false; -let backendInstalled = false; let backendInitialized = false; const port = chrome.runtime.connect({ @@ -21,13 +20,28 @@ const port = chrome.runtime.connect({ }); const handleDisconnect = (): void => { - // console.log('Background disconnected', new Date()); localMessageBus.emit('shutdown'); localMessageBus.destroy(); chromeMessageBus.destroy(); backgroundDisconnected = true; }; +function attemptBackendHandshake() { + if (!backendInitialized) { + // tslint:disable-next-line:no-console + console.log('Attempting handshake with backend', new Date()); + + const retry = () => { + if (backendInitialized || backgroundDisconnected) { + return; + } + handshakeWithBackend(); + setTimeout(retry, 500); + }; + retry(); + } +} + port.onDisconnect.addListener(handleDisconnect); const detectAngularMessageBus = new SamePageMessageBus( @@ -36,11 +50,6 @@ const detectAngularMessageBus = new SamePageMessageBus( ); detectAngularMessageBus.on('detectAngular', (detectionResult) => { - // only install backend once - if (backendInstalled) { - return; - } - if (detectionResult.isAngularDevTools !== true) { return; } @@ -61,7 +70,10 @@ detectAngularMessageBus.on('detectAngular', (detectionResult) => { script.src = chrome.runtime.getURL('app/backend_bundle.js'); document.documentElement.appendChild(script); document.documentElement.removeChild(script); - backendInstalled = true; + + detectAngularMessageBus.emit('backendInstalled'); + + attemptBackendHandshake(); }); const localMessageBus = new SamePageMessageBus(CONTENT_SCRIPT_URI, BACKEND_URI); @@ -71,28 +83,19 @@ const handshakeWithBackend = (): void => { localMessageBus.emit('handshake'); }; +// Relaying messages from FE to BE chromeMessageBus.onAny((topic, args) => { localMessageBus.emit(topic, args); }); +// Relaying messages from BE to FE localMessageBus.onAny((topic, args) => { - backendInitialized = true; chromeMessageBus.emit(topic, args); }); -if (!backendInitialized) { - // tslint:disable-next-line:no-console - console.log('Attempting initialization', new Date()); - - const retry = () => { - if (backendInitialized || backgroundDisconnected) { - return; - } - handshakeWithBackend(); - setTimeout(retry, 500); - }; - retry(); -} +localMessageBus.on('backendReady', () => { + backendInitialized = true; +}); const proxyEventFromWindowToDevToolsExtension = (event: MessageEvent) => { if (event.source === window && event.data && event.data.__NG_DEVTOOLS_EVENT__) { diff --git a/devtools/projects/shell-browser/src/app/detect-angular-for-extension-icon.ts b/devtools/projects/shell-browser/src/app/detect-angular-for-extension-icon.ts index 18d3820d48f6..78cb5ef3bced 100644 --- a/devtools/projects/shell-browser/src/app/detect-angular-for-extension-icon.ts +++ b/devtools/projects/shell-browser/src/app/detect-angular-for-extension-icon.ts @@ -22,6 +22,8 @@ const detectAngularMessageBus = new SamePageMessageBus( CONTENT_SCRIPT_URI, ); +let detectAngularTimeout: ReturnType; + function detectAngular(win: Window): void { const isAngular = appIsAngular(); const isSupportedAngularVersion = appIsSupportedAngularVersion(); @@ -50,7 +52,11 @@ function detectAngular(win: Window): void { }, ]); - setTimeout(() => detectAngular(win), 1000); + detectAngularTimeout = setTimeout(() => detectAngular(win), 1000); } +detectAngularMessageBus.on('backendInstalled', () => { + clearTimeout(detectAngularTimeout); +}); + detectAngular(window); diff --git a/devtools/projects/shell-browser/src/assets/BUILD.bazel b/devtools/projects/shell-browser/src/assets/BUILD.bazel index a408868b845c..0583ffc925ab 100644 --- a/devtools/projects/shell-browser/src/assets/BUILD.bazel +++ b/devtools/projects/shell-browser/src/assets/BUILD.bazel @@ -3,9 +3,7 @@ package(default_visibility = ["//:__subpackages__"]) filegroup( name = "assets", srcs = glob([ - "*.svg", - "**/*.png", - "*.css", + "*.png", ]) + [ "//third_party/fonts.google.com/material-symbols-outlined", ], diff --git a/devtools/projects/shell-browser/src/manifest/manifest.chrome.json b/devtools/projects/shell-browser/src/manifest/manifest.chrome.json index 75fa55d3b1ee..691e7e3a4626 100644 --- a/devtools/projects/shell-browser/src/manifest/manifest.chrome.json +++ b/devtools/projects/shell-browser/src/manifest/manifest.chrome.json @@ -3,7 +3,7 @@ "short_name": "Angular DevTools", "name": "Angular DevTools", "description": "Angular DevTools extends Chrome DevTools adding Angular specific debugging and profiling capabilities.", - "version": "1.6.1", + "version": "1.6.4", "minimum_chrome_version": "102", "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" @@ -25,45 +25,26 @@ "devtools_page": "devtools.html", "web_accessible_resources": [ { - "resources": [ - "app/backend_bundle.js", - "app/detect_angular_for_extension_icon_bundle.js" - ], - "matches": [ - "" - ], + "resources": ["app/backend_bundle.js", "app/detect_angular_for_extension_icon_bundle.js"], + "matches": [""], "extension_ids": [] } ], "background": { "service_worker": "app/background_bundle.js" }, - "permissions": [ - "scripting", - "activeTab", - "storage" - ], - "host_permissions": [ - "" - ], + "permissions": ["scripting", "activeTab", "storage"], + "host_permissions": [""], "content_scripts": [ { - "matches": [ - "" - ], - "js": [ - "app/ng_validate_bundle.js" - ], + "matches": [""], + "js": ["app/ng_validate_bundle.js"], "run_at": "document_idle", "all_frames": true }, { - "matches": [ - "" - ], - "js": [ - "app/content_script_bundle.js" - ], + "matches": [""], + "js": ["app/content_script_bundle.js"], "run_at": "document_idle", "all_frames": true } diff --git a/devtools/projects/shell-browser/src/manifest/manifest.firefox.json b/devtools/projects/shell-browser/src/manifest/manifest.firefox.json index 4e2c97cc7f9f..f1ec9d45e5ba 100644 --- a/devtools/projects/shell-browser/src/manifest/manifest.firefox.json +++ b/devtools/projects/shell-browser/src/manifest/manifest.firefox.json @@ -3,7 +3,7 @@ "short_name": "Angular DevTools", "name": "Angular DevTools", "description": "Angular DevTools extends Firefox DevTools adding Angular specific debugging and profiling capabilities.", - "version": "1.6.1", + "version": "1.6.4", "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "icons": { "16": "assets/icon16.png", @@ -14,40 +14,21 @@ "default_popup": "popups/not-angular.html" }, "devtools_page": "devtools.html", - "web_accessible_resources": [ - "app/backend_bundle.js", - "devtools.html" - ], + "web_accessible_resources": ["app/backend_bundle.js", "devtools.html"], "background": { - "scripts": [ - "app/background_bundle.js" - ] + "scripts": ["app/background_bundle.js"] }, - "permissions": [ - "activeTab", - "storage", - "http://*/*", - "https://*/*", - "file:///*" - ], + "permissions": ["activeTab", "storage", "http://*/*", "https://*/*", "file:///*"], "content_scripts": [ { - "matches": [ - "" - ], - "js": [ - "app/ng_validate_bundle.js" - ], + "matches": [""], + "js": ["app/ng_validate_bundle.js"], "run_at": "document_idle", "all_frames": true }, { - "matches": [ - "" - ], - "js": [ - "app/content_script_bundle.js" - ], + "matches": [""], + "js": ["app/content_script_bundle.js"], "run_at": "document_idle", "all_frames": true } diff --git a/devtools/src/app/app.component.ts b/devtools/src/app/app.component.ts index 176d3d4c92fa..2057efd1cb07 100644 --- a/devtools/src/app/app.component.ts +++ b/devtools/src/app/app.component.ts @@ -31,11 +31,10 @@ export class EmptyComponent { @Component({ selector: 'other-app', template: ` - @defer { - - } - @placeholder (minimum 2s) { - Stuff will be loaded here + @defer { + + } @placeholder (minimum 2s) { + Stuff will be loaded here } `, imports: [EmptyComponent], diff --git a/devtools/src/app/demo-app/cookies.component.ts b/devtools/src/app/demo-app/cookies.component.ts index 55bf135dd980..67d79c10836c 100644 --- a/devtools/src/app/demo-app/cookies.component.ts +++ b/devtools/src/app/demo-app/cookies.component.ts @@ -14,9 +14,9 @@ import {Component, signal, computed} from '@angular/core';

    Cookie recipe

    Butter: {{ butter() }} cup(s)

    diff --git a/devtools/src/app/demo-app/todo/home/todos.component.html b/devtools/src/app/demo-app/todo/home/todos.component.html index cb16e3d5b0a6..6efb35d588a5 100644 --- a/devtools/src/app/demo-app/todo/home/todos.component.html +++ b/devtools/src/app/demo-app/todo/home/todos.component.html @@ -2,7 +2,7 @@

    {{ 'Sample text processed by a pipe' | sample }}

    -

    {{title}}

    +

    {{ title }}

    { @Component({ selector: 'base-case', - template: ` -

    Base Case

    - `, + template: `

    Base Case

    `, }) export class BaseCaseComponent { // This component serves as a base case for the recursive component. @@ -46,10 +44,10 @@ export class BaseCaseComponent { template: `
    @if (level() === 0) { - + } @else {

    Level {{ level() }}

    - + }
    `, diff --git a/devtools/src/app/demo-app/todo/routes/routes.module.ts b/devtools/src/app/demo-app/todo/routes/routes.module.ts index bd099155e776..0b80358ce2c8 100644 --- a/devtools/src/app/demo-app/todo/routes/routes.module.ts +++ b/devtools/src/app/demo-app/todo/routes/routes.module.ts @@ -100,6 +100,13 @@ export function customRerunLogic() { component: RoutesHomeComponent, data: { message: 'Hello from route!!', + nested: { + foo: 'bar', + baz: { + qux: 42, + quux: [3, 1, 4], + }, + }, }, }, { diff --git a/devtools/tools/release.mts b/devtools/tools/release.mts index ebcc3feb948a..b260b2710e02 100644 --- a/devtools/tools/release.mts +++ b/devtools/tools/release.mts @@ -279,6 +279,9 @@ async function updateManifests(newVersion: string): Promise { await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n'); console.log(`- Updated version for manifest in: ${manifestPath}`); } + + await exec(`pnpm ng-dev format files ${manifestPaths.map((f) => `"${f}"`).join(' ')}`); + console.log(chalk.green('Manifest files updated successfully.')); } diff --git a/goldens/public-api/core/errors.api.md b/goldens/public-api/core/errors.api.md index 9677a42531cf..56bb883d919b 100644 --- a/goldens/public-api/core/errors.api.md +++ b/goldens/public-api/core/errors.api.md @@ -34,6 +34,8 @@ export const enum RuntimeErrorCode { // (undocumented) CYCLIC_DI_DEPENDENCY = -200, // (undocumented) + DEF_TYPE_UNDEFINED = -919, + // (undocumented) DEFER_IN_HMR_MODE = -751, // (undocumented) DEFER_LOADING_FAILED = -750, @@ -186,6 +188,8 @@ export const enum RuntimeErrorCode { // (undocumented) UNKNOWN_ELEMENT = 304, // (undocumented) + UNSAFE_ATTRIBUTE_BINDING = -910, + // @deprecated (undocumented) UNSAFE_IFRAME_ATTRS = -910, // (undocumented) UNSAFE_VALUE_IN_RESOURCE_URL = 904, diff --git a/goldens/public-api/core/index.api.md b/goldens/public-api/core/index.api.md index 4fc47fd3eeb3..ce9a39c92a93 100644 --- a/goldens/public-api/core/index.api.md +++ b/goldens/public-api/core/index.api.md @@ -884,9 +884,13 @@ export const Injectable: InjectableDecorator; // @public export interface InjectableDecorator { (): TypeDecorator; + // @deprecated (undocumented) + (options?: { + providedIn: Type | 'any'; + } & InjectableProvider): TypeDecorator; // (undocumented) (options?: { - providedIn: Type | 'root' | 'platform' | 'any' | null; + providedIn: 'root' | 'platform' | null; } & InjectableProvider): TypeDecorator; // (undocumented) new (): Injectable; @@ -916,6 +920,11 @@ export interface InjectDecorator { // @public export class InjectionToken { + // @deprecated + constructor(_desc: string, options: { + providedIn: Type | 'any'; + factory: () => T; + }); constructor(_desc: string, options?: { providedIn?: Type | 'root' | 'platform' | 'any' | null; factory: () => T; @@ -1476,7 +1485,7 @@ export function provideEnvironmentInitializer(initializerFn: () => void): Enviro export function provideNgReflectAttributes(): EnvironmentProviders; // @public -export function providePlatformInitializer(initializerFn: () => void): EnvironmentProviders; +export function providePlatformInitializer(initializerFn: () => void): StaticProvider; // @public export type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | ExistingProvider | FactoryProvider | any[]; @@ -1484,6 +1493,9 @@ export type Provider = TypeProvider | ValueProvider | ClassProvider | Constructo // @public export type ProviderToken = Type | AbstractType | InjectionToken; +// @public +export function provideStabilityDebugging(): EnvironmentProviders; + // @public export function provideZoneChangeDetection(options?: NgZoneOptions): EnvironmentProviders; @@ -1670,7 +1682,13 @@ export type ResourceStreamItem = { }; // @public -export const RESPONSE_INIT: InjectionToken; +export const RESPONSE_INIT: InjectionToken; + +// @public +type ResponseInit_2 = { + -readonly [P in keyof globalThis.ResponseInit]: globalThis.ResponseInit[P]; +}; +export { ResponseInit_2 as ResponseInit } // @public export function runInInjectionContext(injector: Injector, fn: () => ReturnT): ReturnT; diff --git a/goldens/public-api/forms/signals/compat/index.api.md b/goldens/public-api/forms/signals/compat/index.api.md index 18f30396d224..33f0094ef749 100644 --- a/goldens/public-api/forms/signals/compat/index.api.md +++ b/goldens/public-api/forms/signals/compat/index.api.md @@ -17,9 +17,9 @@ import { ValidationErrors } from '@angular/forms'; import { ValidatorFn } from '@angular/forms'; import { WritableSignal } from '@angular/core'; import { ɵCONTROL } from '@angular/core'; -import { ɵControl } from '@angular/core'; +import { ɵcontrolUpdate } from '@angular/core'; import { ɵFieldState } from '@angular/core'; -import { ɵInteropControl } from '@angular/core'; +import { ɵɵcontrolCreate } from '@angular/core'; // @public export function compatForm(model: WritableSignal): FieldTree; @@ -45,7 +45,7 @@ export class CompatValidationError implements ValidationError { // (undocumented) readonly control: AbstractControl; // (undocumented) - readonly field: FieldTree; + readonly fieldTree: FieldTree; // (undocumented) readonly kind: string; // (undocumented) diff --git a/goldens/public-api/forms/signals/index.api.md b/goldens/public-api/forms/signals/index.api.md index b26ce25fd248..b323a5558a51 100644 --- a/goldens/public-api/forms/signals/index.api.md +++ b/goldens/public-api/forms/signals/index.api.md @@ -15,6 +15,7 @@ import * as i0 from '@angular/core'; import { InjectionToken } from '@angular/core'; import { Injector } from '@angular/core'; import { InputSignal } from '@angular/core'; +import { InputSignalWithTransform } from '@angular/core'; import { ModelSignal } from '@angular/core'; import { NgControl } from '@angular/forms'; import { OutputRef } from '@angular/core'; @@ -26,23 +27,9 @@ import { ValidationErrors } from '@angular/forms'; import { ValidatorFn } from '@angular/forms'; import { WritableSignal } from '@angular/core'; import { ɵCONTROL } from '@angular/core'; -import { ɵControl } from '@angular/core'; +import { ɵcontrolUpdate } from '@angular/core'; import { ɵFieldState } from '@angular/core'; -import { ɵInteropControl } from '@angular/core'; - -// @public -export function aggregateMetadata(path: SchemaPath, key: AggregateMetadataKey, logic: NoInfer>): void; - -// @public -export class AggregateMetadataKey { - // (undocumented) - readonly getInitial: () => TAcc; - // (undocumented) - readonly reduce: (acc: TAcc, item: TItem) => TAcc; -} - -// @public -export function andMetadataKey(): AggregateMetadataKey; +import { ɵɵcontrolCreate } from '@angular/core'; // @public export function apply(path: SchemaPath, schema: NoInfer>): void; @@ -91,7 +78,16 @@ export type CompatSchemaPath(): MetadataKey; +export function createManagedMetadataKey(create: (s: Signal) => TRead): MetadataKey; + +// @public +export function createManagedMetadataKey(create: (s: Signal) => TRead, reducer: MetadataReducer): MetadataKey; + +// @public +export function createMetadataKey(): MetadataKey, TWrite, TWrite | undefined>; + +// @public +export function createMetadataKey(reducer: MetadataReducer): MetadataKey, TWrite, TAcc>; // @public export function customError>(obj: WithField): CustomValidationError; @@ -103,7 +99,7 @@ export function customError>(obj?: export class CustomValidationError implements ValidationError { constructor(options?: ValidationErrorOptions); [key: PropertyKey]: unknown; - readonly field: FieldTree; + readonly fieldTree: FieldTree; readonly kind: string; readonly message?: string; } @@ -119,7 +115,7 @@ export function disabled(pat // @public export interface DisabledReason { - readonly field: FieldTree; + readonly fieldTree: FieldTree; readonly message?: string; } @@ -142,23 +138,25 @@ export class EmailValidationError extends _NgValidationError { export const FIELD: InjectionToken>; // @public -export class Field implements ɵControl { +export class Field { // (undocumented) - readonly [ɵCONTROL]: undefined; + readonly [ɵCONTROL]: { + readonly create: typeof ɵɵcontrolCreate; + readonly update: typeof ɵcontrolUpdate; + }; // (undocumented) - readonly classes: (readonly [string, i0.Signal])[]; + readonly element: HTMLElement; // (undocumented) readonly field: i0.InputSignal>; protected getOrCreateNgControl(): InteropNgControl; // (undocumented) + readonly injector: Injector; + // (undocumented) readonly state: i0.Signal<[T] extends [_angular_forms.AbstractControl] ? CompatFieldState : FieldState>; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration, "[field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration, never>; - get ɵinteropControl(): ɵInteropControl | undefined; - // (undocumented) - ɵregister(): void; } // @public @@ -173,12 +171,10 @@ export interface FieldState; readonly errorSummary: Signal; readonly fieldBindings: Signal[]>; - hasMetadata(key: MetadataKey | AggregateMetadataKey): boolean; readonly hidden: Signal; readonly invalid: Signal; readonly keyInParent: Signal; - metadata(key: AggregateMetadataKey): Signal; - metadata(key: MetadataKey): M | undefined; + metadata(key: MetadataKey): M | undefined; readonly pending: Signal; reset(value?: TValue): void; readonly submitting: Signal; @@ -216,22 +212,22 @@ export interface FormOptions { // @public export interface FormUiControl { - readonly dirty?: InputSignal; - readonly disabled?: InputSignal; - readonly disabledReasons?: InputSignal[]>; - readonly errors?: InputSignal[]>; - readonly hidden?: InputSignal; - readonly invalid?: InputSignal; - readonly max?: InputSignal; - readonly maxLength?: InputSignal; - readonly min?: InputSignal; - readonly minLength?: InputSignal; - readonly name?: InputSignal; - readonly pattern?: InputSignal; - readonly pending?: InputSignal; - readonly readonly?: InputSignal; - readonly required?: InputSignal; - readonly touched?: ModelSignal | InputSignal | OutputRef; + readonly dirty?: InputSignal | InputSignalWithTransform; + readonly disabled?: InputSignal | InputSignalWithTransform; + readonly disabledReasons?: InputSignal[]> | InputSignalWithTransform[], unknown>; + readonly errors?: InputSignal[]> | InputSignalWithTransform[], unknown>; + readonly hidden?: InputSignal | InputSignalWithTransform; + readonly invalid?: InputSignal | InputSignalWithTransform; + readonly max?: InputSignal | InputSignalWithTransform; + readonly maxLength?: InputSignal | InputSignalWithTransform; + readonly min?: InputSignal | InputSignalWithTransform; + readonly minLength?: InputSignal | InputSignalWithTransform; + readonly name?: InputSignal | InputSignalWithTransform; + readonly pattern?: InputSignal | InputSignalWithTransform; + readonly pending?: InputSignal | InputSignalWithTransform; + readonly readonly?: InputSignal | InputSignalWithTransform; + readonly required?: InputSignal | InputSignalWithTransform; + readonly touched?: ModelSignal | InputSignal | InputSignalWithTransform | OutputRef; } // @public @@ -264,9 +260,6 @@ export interface ItemFieldContext extends ChildFieldContext { // @public export type ItemType = T extends ReadonlyArray ? T[number] : T[keyof T]; -// @public -export function listMetadataKey(): AggregateMetadataKey; - // @public export type LogicFn = (ctx: FieldContext) => TReturn; @@ -274,13 +267,13 @@ export type LogicFn export type MapToErrorsFn = (result: TResult, ctx: FieldContext) => TreeValidationResult; // @public -export const MAX: AggregateMetadataKey; +export const MAX: MetadataKey, number | undefined, number | undefined>; // @public export function max(path: SchemaPath, maxValue: number | LogicFn, config?: BaseValidatorConfig): void; // @public -export const MAX_LENGTH: AggregateMetadataKey; +export const MAX_LENGTH: MetadataKey, number | undefined, number | undefined>; // @public export function maxError(max: number, options: WithField): MaxValidationError; @@ -306,9 +299,6 @@ export class MaxLengthValidationError extends _NgValidationError { readonly maxLength: number; } -// @public -export function maxMetadataKey(): AggregateMetadataKey; - // @public export class MaxValidationError extends _NgValidationError { constructor(max: number, options?: ValidationErrorOptions); @@ -325,23 +315,44 @@ export type MaybeFieldTree = (TModel & undefined) | SchemaPathTree, TPathKind>; // @public -export function metadata(path: SchemaPath, factory: (ctx: FieldContext) => TData): MetadataKey; +export function metadata, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath, key: TKey, logic: NoInfer, TPathKind>>): TKey; // @public -export function metadata(path: SchemaPath, key: MetadataKey, factory: (ctx: FieldContext) => TData): MetadataKey; +export class MetadataKey { + protected constructor(reducer: MetadataReducer, create: ((s: Signal) => TRead) | undefined); + // (undocumented) + readonly create: ((s: Signal) => TRead) | undefined; + // (undocumented) + readonly reducer: MetadataReducer; +} // @public -export class MetadataKey { +export interface MetadataReducer { + getInitial: () => TAcc; + reduce: (acc: TAcc, item: TItem) => TAcc; } +// @public (undocumented) +export const MetadataReducer: { + readonly list: () => MetadataReducer; + readonly min: () => MetadataReducer; + readonly max: () => MetadataReducer; + readonly or: () => MetadataReducer; + readonly and: () => MetadataReducer; + readonly override: typeof override; +}; + +// @public +export type MetadataSetterType = TKey extends MetadataKey ? TWrite : never; + // @public -export const MIN: AggregateMetadataKey; +export const MIN: MetadataKey, number | undefined, number | undefined>; // @public export function min(path: SchemaPath, minValue: number | LogicFn, config?: BaseValidatorConfig): void; // @public -export const MIN_LENGTH: AggregateMetadataKey; +export const MIN_LENGTH: MetadataKey, number | undefined, number | undefined>; // @public export function minError(min: number, options: WithField): MinValidationError; @@ -367,9 +378,6 @@ export class MinLengthValidationError extends _NgValidationError { readonly minLength: number; } -// @public -export function minMetadataKey(): AggregateMetadataKey; - // @public export class MinValidationError extends _NgValidationError { constructor(min: number, options?: ValidationErrorOptions); @@ -388,9 +396,6 @@ export type NgValidationError = RequiredValidationError | MinValidationError | M // @public export type OneOrMany = T | readonly T[]; -// @public -export function orMetadataKey(): AggregateMetadataKey; - // @public export type PathKind = PathKind.Root | PathKind.Child | PathKind.Item; @@ -410,7 +415,7 @@ export namespace PathKind { } // @public -export const PATTERN: AggregateMetadataKey; +export const PATTERN: MetadataKey, RegExp | undefined, RegExp[]>; // @public export function pattern(path: SchemaPath, pattern: RegExp | LogicFn, config?: BaseValidatorConfig): void; @@ -439,14 +444,11 @@ export function readonly(pat // @public export type ReadonlyArrayLike = Pick, number | 'length' | typeof Symbol.iterator>; -// @public -export function reducedMetadataKey(reduce: (acc: TAcc, item: TItem) => TAcc, getInitial: NoInfer<() => TAcc>): AggregateMetadataKey; - // @public export type RemoveStringIndexUnknownKey = string extends K ? unknown extends V ? never : K : K; // @public -export const REQUIRED: AggregateMetadataKey; +export const REQUIRED: MetadataKey, boolean, boolean>; // @public export function required(path: SchemaPath, config?: BaseValidatorConfig & { @@ -467,7 +469,7 @@ export class RequiredValidationError extends _NgValidationError { // @public export interface RootFieldContext { - readonly field: FieldTree; + readonly fieldTree: FieldTree; fieldTreeOf(p: SchemaPathTree): FieldTree; readonly pathKeys: Signal; readonly state: FieldState; @@ -518,7 +520,7 @@ export type SchemaPathTree = // @public export interface SignalFormsConfig { classes?: { - [className: string]: (state: FieldState) => boolean; + [className: string]: (state: Field) => boolean; }; } @@ -580,10 +582,10 @@ export interface ValidationError { // @public (undocumented) export namespace ValidationError { export interface WithField extends ValidationError { - readonly field: FieldTree; + readonly fieldTree: FieldTree; } export interface WithOptionalField extends ValidationError { - readonly field?: FieldTree; + readonly fieldTree?: FieldTree; } export interface WithoutField extends ValidationError { readonly field?: never; @@ -601,17 +603,17 @@ export type Validator = Logi // @public export type WithField = T & { - field: FieldTree; + fieldTree: FieldTree; }; // @public -export type WithOptionalField = Omit & { - field?: FieldTree; +export type WithOptionalField = Omit & { + fieldTree?: FieldTree; }; // @public export type WithoutField = T & { - field: never; + fieldTree: never; }; // (No @packageDocumentation comment for this package) diff --git a/integration/cli-hello-world-ivy-i18n/package.json b/integration/cli-hello-world-ivy-i18n/package.json index 551830a6a179..74d5d1e40c58 100644 --- a/integration/cli-hello-world-ivy-i18n/package.json +++ b/integration/cli-hello-world-ivy-i18n/package.json @@ -27,14 +27,14 @@ "zone.js": "0.16.0" }, "devDependencies": { - "@angular-devkit/build-angular": "21.1.0-next.0", - "@angular/build": "21.1.0-next.0", - "@angular/cli": "21.1.0-next.0", + "@angular-devkit/build-angular": "21.1.0-next.2", + "@angular/build": "21.1.0-next.2", + "@angular/cli": "21.1.0-next.2", "@angular/compiler-cli": "link:./in-existing-linked-by-bazel", "@types/jasmine": "^5.0.0", "@types/jasminewd2": "^2.0.8", "@types/node": "^20.14.8", - "jasmine-core": "5.12.1", + "jasmine-core": "5.13.0", "jasmine-spec-reporter": "~7.0.0", "npm-run-all": "4.1.5", "protractor": "^7.0.0", @@ -42,5 +42,5 @@ "ts-node": "^10.9.1", "typescript": "5.9.3" }, - "packageManager": "pnpm@10.23.0" + "packageManager": "pnpm@10.26.0" } diff --git a/integration/cli-hello-world-ivy-i18n/pnpm-lock.yaml b/integration/cli-hello-world-ivy-i18n/pnpm-lock.yaml index e97fa57f1f17..7786ac2ff13f 100644 --- a/integration/cli-hello-world-ivy-i18n/pnpm-lock.yaml +++ b/integration/cli-hello-world-ivy-i18n/pnpm-lock.yaml @@ -40,14 +40,14 @@ importers: version: 0.16.0 devDependencies: '@angular-devkit/build-angular': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(protractor@7.0.0)(typescript@5.9.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(jiti@2.6.1)(protractor@7.0.0)(typescript@5.9.3) '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) '@angular/cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@types/node@20.19.25) '@angular/compiler-cli': specifier: link:./in-existing-linked-by-bazel version: link:in-existing-linked-by-bazel @@ -61,8 +61,8 @@ importers: specifier: ^20.14.8 version: 20.19.25 jasmine-core: - specifier: 5.12.1 - version: 5.12.1 + specifier: 5.13.0 + version: 5.13.0 jasmine-spec-reporter: specifier: ~7.0.0 version: 7.0.0 @@ -84,72 +84,72 @@ importers: packages: - '@algolia/abtesting@1.11.0': - resolution: {integrity: sha512-a7oQ8dwiyoyVmzLY0FcuBqyqcNSq78qlcOtHmNBumRlHCSnXDcuoYGBGPN1F6n8JoGhviDDsIaF/oQrzTzs6Lg==} + '@algolia/abtesting@1.12.0': + resolution: {integrity: sha512-EfW0bfxjPs+C7ANkJDw2TATntfBKsFiy7APh+KO0pQ8A6HYa5I0NjFuCGCXWfzzzLXNZta3QUl3n5Kmm6aJo9Q==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.45.0': - resolution: {integrity: sha512-WTW0VZA8xHMbzuQD5b3f41ovKZ0MNTIXkWfm0F2PU+XGcLxmxX15UqODzF2sWab0vSbi3URM1xLhJx+bXbd1eQ==} + '@algolia/client-abtesting@5.46.0': + resolution: {integrity: sha512-eG5xV8rujK4ZIHXrRshvv9O13NmU/k42Rnd3w43iKH5RaQ2zWuZO6Q7XjaoJjAFVCsJWqRbXzbYyPGrbF3wGNg==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.45.0': - resolution: {integrity: sha512-I3g7VtvG/QJOH3tQO7E7zWTwBfK/nIQXShFLR8RvPgWburZ626JNj332M3wHCYcaAMivN9WJG66S2JNXhm6+Xg==} + '@algolia/client-analytics@5.46.0': + resolution: {integrity: sha512-AYh2uL8IUW9eZrbbT+wZElyb7QkkeV3US2NEKY7doqMlyPWE8lErNfkVN1NvZdVcY4/SVic5GDbeDz2ft8YIiQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.45.0': - resolution: {integrity: sha512-/nTqm1tLiPtbUr+8kHKyFiCOfhRfgC+JxLvOCq471gFZZOlsh6VtFRiKI60/zGmHTojFC6B0mD80PB7KeK94og==} + '@algolia/client-common@5.46.0': + resolution: {integrity: sha512-0emZTaYOeI9WzJi0TcNd2k3SxiN6DZfdWc2x2gHt855Jl9jPUOzfVTL6gTvCCrOlT4McvpDGg5nGO+9doEjjig==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.45.0': - resolution: {integrity: sha512-suQTx/1bRL1g/K2hRtbK3ANmbzaZCi13487sxxmqok+alBDKKw0/TI73ZiHjjFXM2NV52inwwcmW4fUR45206Q==} + '@algolia/client-insights@5.46.0': + resolution: {integrity: sha512-wrBJ8fE+M0TDG1As4DDmwPn2TXajrvmvAN72Qwpuv8e2JOKNohF7+JxBoF70ZLlvP1A1EiH8DBu+JpfhBbNphQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.45.0': - resolution: {integrity: sha512-CId/dbjpzI3eoUhPU6rt/z4GrRsDesqFISEMOwrqWNSrf4FJhiUIzN42Ac+Gzg69uC0RnzRYy60K1y4Na5VSMw==} + '@algolia/client-personalization@5.46.0': + resolution: {integrity: sha512-LnkeX4p0ENt0DoftDJJDzQQJig/sFQmD1eQifl/iSjhUOGUIKC/7VTeXRcKtQB78naS8njUAwpzFvxy1CDDXDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.45.0': - resolution: {integrity: sha512-tjbBKfA8fjAiFtvl9g/MpIPiD6pf3fj7rirVfh1eMIUi8ybHP4ovDzIaE216vHuRXoePQVCkMd2CokKvYq1CLw==} + '@algolia/client-query-suggestions@5.46.0': + resolution: {integrity: sha512-aF9tc4ex/smypXw+W3lBPB1jjKoaGHpZezTqofvDOI/oK1dR2sdTpFpK2Ru+7IRzYgwtRqHF3znmTlyoNs9dpA==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.45.0': - resolution: {integrity: sha512-nxuCid+Nszs4xqwIMDw11pRJPes2c+Th1yup/+LtpjFH8QWXkr3SirNYSD3OXAeM060HgWWPLA8/Fxk+vwxQOA==} + '@algolia/client-search@5.46.0': + resolution: {integrity: sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.45.0': - resolution: {integrity: sha512-t+1doBzhkQTeOOjLHMlm4slmXBhvgtEGQhOmNpMPTnIgWOyZyESWdm+XD984qM4Ej1i9FRh8VttOGrdGnAjAng==} + '@algolia/ingestion@1.46.0': + resolution: {integrity: sha512-2LT0/Z+/sFwEpZLH6V17WSZ81JX2uPjgvv5eNlxgU7rPyup4NXXfuMbtCJ+6uc4RO/LQpEJd3Li59ke3wtyAsA==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.45.0': - resolution: {integrity: sha512-IaX3ZX1A/0wlgWZue+1BNWlq5xtJgsRo7uUk/aSiYD7lPbJ7dFuZ+yTLFLKgbl4O0QcyHTj1/mSBj9ryF1Lizg==} + '@algolia/monitoring@1.46.0': + resolution: {integrity: sha512-uivZ9wSWZ8mz2ZU0dgDvQwvVZV8XBv6lYBXf8UtkQF3u7WeTqBPeU8ZoeTyLpf0jAXCYOvc1mAVmK0xPLuEwOQ==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.45.0': - resolution: {integrity: sha512-1jeMLoOhkgezCCPsOqkScwYzAAc1Jr5T2hisZl0s32D94ZV7d1OHozBukgOjf8Dw+6Hgi6j52jlAdUWTtkX9Mg==} + '@algolia/recommend@5.46.0': + resolution: {integrity: sha512-O2BB8DuySuddgOAbhyH4jsGbL+KyDGpzJRtkDZkv091OMomqIA78emhhMhX9d/nIRrzS1wNLWB/ix7Hb2eV5rg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.45.0': - resolution: {integrity: sha512-46FIoUkQ9N7wq4/YkHS5/W9Yjm4Ab+q5kfbahdyMpkBPJ7IBlwuNEGnWUZIQ6JfUZuJVojRujPRHMihX4awUMg==} + '@algolia/requester-browser-xhr@5.46.0': + resolution: {integrity: sha512-eW6xyHCyYrJD0Kjk9Mz33gQ40LfWiEA51JJTVfJy3yeoRSw/NXhAL81Pljpa0qslTs6+LO/5DYPZddct6HvISQ==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.45.0': - resolution: {integrity: sha512-XFTSAtCwy4HdBhSReN2rhSyH/nZOM3q3qe5ERG2FLbYId62heIlJBGVyAPRbltRwNlotlydbvSJ+SQ0ruWC2cw==} + '@algolia/requester-fetch@5.46.0': + resolution: {integrity: sha512-Vn2+TukMGHy4PIxmdvP667tN/MhS7MPT8EEvEhS6JyFLPx3weLcxSa1F9gVvrfHWCUJhLWoMVJVB2PT8YfRGcw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.45.0': - resolution: {integrity: sha512-8mTg6lHx5i44raCU52APsu0EqMsdm4+7Hch/e4ZsYZw0hzwkuaMFh826ngnkYf9XOl58nHoou63aZ874m8AbpQ==} + '@algolia/requester-node-http@5.46.0': + resolution: {integrity: sha512-xaqXyna5yBZ+r1SJ9my/DM6vfTqJg9FJgVydRJ0lnO+D5NhqGW/qaRG/iBGKr/d4fho34el6WakV7BqJvrl/HQ==} engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2101.0-next.0': - resolution: {integrity: sha512-aExU6NQA1RDKqH9oecf+YBSZE7UlsdsWKqMJ8Me8Zj7PzMy6CNu82xL1Cv4yRB3xTHfNlB+CSnwnU6FoOoGohw==} + '@angular-devkit/architect@0.2101.0-next.2': + resolution: {integrity: sha512-Lm8VsC+FmZ29/iZ4fjyAqp+kFjzY+UPUtIAFcckux0ss63JxOWxBhgU3QL8EwvGCnaUuYO7XoBwG3fV9dNp0fA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/build-angular@21.1.0-next.0': - resolution: {integrity: sha512-WT1Bzm0KLS+5me7Cup5o0iM6JaPjU9yP8S4AVX54ikHEtuiw+dxiait7aEAapMlcO8Wt2m8FMC+X0RChReAtJA==} + '@angular-devkit/build-angular@21.1.0-next.2': + resolution: {integrity: sha512-fZvBzacz/NpSRvN0eSRzZXm5XIuZtd/FLosjsqcNcVM05pK1SxC7foHoW1nOQ9S4W4lq9inLpD4VNN4AZgH04A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -158,7 +158,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 '@web/test-runner': ^0.20.0 browser-sync: ^3.0.2 jest: ^30.2.0 @@ -198,28 +198,28 @@ packages: tailwindcss: optional: true - '@angular-devkit/build-webpack@0.2101.0-next.0': - resolution: {integrity: sha512-eLnDxsc3YfdLjl+0FPPunEjvmwdhmH28aiZIQxqN0ULH/iKercdjuI51ilwG/fS+UUpIxazXcyHqv9MjL0Sc/Q==} + '@angular-devkit/build-webpack@0.2101.0-next.2': + resolution: {integrity: sha512-lj2zbp9Xo2qPm3qGRswYsOzZ8ED9HExMTHt9pDDzRcVpnKCqQGSINmJVndnD8xLVt92S39Cpr1QBOsE6U9FKXQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - '@angular-devkit/core@21.1.0-next.0': - resolution: {integrity: sha512-a48yNgGp79R23HPekTjjcwgRrtJEDndN1nBs0j42qgg+jSM/cqeTw7rMjNPAgNv9dkqxn3Rk/G1WGKIEHANlkQ==} + '@angular-devkit/core@21.1.0-next.2': + resolution: {integrity: sha512-SavlqKJmM+EqX1eg4iuboaqbJTBRALuwuocctSBmowOoYFYjJXaJbdit1fwB0vqQ946RKG56YYCcAEuIhjmNwA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - chokidar: ^4.0.0 + chokidar: ^5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@21.1.0-next.0': - resolution: {integrity: sha512-fi8zVvjGkrW3DQTB8NEHvKVQpW62B1o3ZMazRYRTwxMlqJiWn+eCU3EzuVw1ScgxlvnHeacesxIn9OPovGFIug==} + '@angular-devkit/schematics@21.1.0-next.2': + resolution: {integrity: sha512-01krelZX9UC8l8BlkIpExQ8fIekvtnSk0831j9dbWxbhKu/QZa8IHbKsB14Ph6NNvlz84FmUFhZpODMwjS831w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular/build@21.1.0-next.0': - resolution: {integrity: sha512-2vH+9Caw75JnviZ97mibOLPaTwJgHxjVuYoE9za5Arm9Up4NsCakui//V379qkLobsEAnln0huh8g0PC+mJ4Jg==} + '@angular/build@21.1.0-next.2': + resolution: {integrity: sha512-WHbkVNH5t+OyOd3ciWKxMhAe9IdTGMqUxH+HasiJ+MYcwQ4SCqL4GWsOPl+UiJrOUKa4At9yd5lnOpjz1kWrWA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^21.0.0 || ^21.1.0-next.0 @@ -229,7 +229,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 || ^21.1.0-next.0 @@ -264,8 +264,8 @@ packages: vitest: optional: true - '@angular/cli@21.1.0-next.0': - resolution: {integrity: sha512-QpboTD5qCFjw1SRPjnVWJQ7c2s4Co5rnHSvVWuAixtM4rlNkulzgzcAndO4HqjnBq4Q8RRs8V1L/JeW71MSfwQ==} + '@angular/cli@21.1.0-next.2': + resolution: {integrity: sha512-SLBX5G187ih0gu+SoXoQ6wPJcmbZtVwjD705YEPH4YD1vsv1IP8YOwGlIV5SHbv7A4cM+wL1S1HNHD/e950iJQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true @@ -797,8 +797,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -809,8 +809,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -821,8 +821,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -833,8 +833,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -845,8 +845,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -857,8 +857,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -869,8 +869,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': - resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -881,8 +881,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -893,8 +893,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -905,8 +905,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -917,8 +917,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -929,8 +929,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -941,8 +941,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -953,8 +953,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -965,8 +965,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.0': - resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -977,8 +977,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -989,8 +989,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1001,8 +1001,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1013,8 +1013,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.0': - resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1025,8 +1025,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1037,8 +1037,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.0': - resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1049,8 +1049,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': - resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1061,8 +1061,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1073,8 +1073,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1085,8 +1085,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1097,8 +1097,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1356,11 +1356,12 @@ packages: cpu: [x64] os: [win32] - '@modelcontextprotocol/sdk@1.22.0': - resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} + '@modelcontextprotocol/sdk@1.24.3': + resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -1508,11 +1509,11 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} - '@ngtools/webpack@21.1.0-next.0': - resolution: {integrity: sha512-lFlrmQARJJKwlYfoGGXKrr7Bbk5FJzx6xCEgw6ZqnH5FjIaxHrXZ+jLmoEeqUKeX0NSuX5q0oLpE40IExFh2FA==} + '@ngtools/webpack@21.1.0-next.2': + resolution: {integrity: sha512-EFou/qczcYYaKFunyl4iLp6tJqClwO2lngesO6eDRlV2LdRNlB0uImmurNyluaM3vKkLvRkRVlzq0G4Q6kQTUw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -1556,8 +1557,8 @@ packages: resolution: {integrity: sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==} engines: {node: ^20.17.0 || >=22.9.0} - '@oxc-project/types@0.98.0': - resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==} + '@oxc-project/types@0.101.0': + resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==} '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} @@ -1647,95 +1648,89 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} - '@rolldown/binding-android-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==} + '@rolldown/binding-android-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.51': - resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==} + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': - resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': - resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': - resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.51': - resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==} + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/rollup-android-arm-eabi@4.53.3': resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} @@ -1858,8 +1853,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@21.1.0-next.0': - resolution: {integrity: sha512-TBSh8b8+OdlgWkJlTTv/JRwrPTRH+mpJ32AlKRBimDChPDVP/+MAKlF7tsSbXKsi+V9mVWuBXcVR1A0R87mjkw==} + '@schematics/angular@21.1.0-next.2': + resolution: {integrity: sha512-NqVq5MbwNhJ5Phmv1pzj8ZvIVoGi3e4a702VbBlS6KxoZI9j8ulBh8ZnAWN3q6ZkSRnOIuPqvgowfeNbRtplFg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': @@ -2129,8 +2124,8 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@5.45.0: - resolution: {integrity: sha512-wrj4FGr14heLOYkBKV3Fbq5ZBGuIFeDJkTilYq/G+hH1CSlQBtYvG2X1j67flwv0fUeQJwnWxxRIunSemAZirA==} + algoliasearch@5.46.0: + resolution: {integrity: sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==} engines: {node: '>= 14.0.0'} ansi-align@3.0.1: @@ -2272,8 +2267,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.31: - resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} + baseline-browser-mapping@2.8.32: + resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==} hasBin: true batch@0.6.1: @@ -2302,8 +2297,8 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} bonjour-service@1.3.0: @@ -2377,8 +2372,8 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -2741,8 +2736,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.259: - resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.262: + resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2831,8 +2826,8 @@ packages: es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - esbuild-wasm@0.27.0: - resolution: {integrity: sha512-4XpLDOY4sOzqgiezPXDkHTn25cBA1MKN5OTotehoFR7/wTpSYCK76OA8rQfpiuz12G27JpGxxdh+/D9GcNl61w==} + esbuild-wasm@0.27.1: + resolution: {integrity: sha512-NjueSyuuMjX6F/mnqQ8g4rkwAFTct7JT/A/oXjXhGTFbWiTm3zU59BMulOrT+KuCHj+oShTBARQCxCDDvVxwFA==} engines: {node: '>=18'} hasBin: true @@ -2841,8 +2836,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} engines: {node: '>=18'} hasBin: true @@ -3301,8 +3296,8 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} is-array-buffer@3.0.5: @@ -3531,8 +3526,8 @@ packages: jasmine-core@2.8.0: resolution: {integrity: sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==} - jasmine-core@5.12.1: - resolution: {integrity: sha512-P/UbRZ0LKwXe7wEpwDheuhunPwITn4oPALhrJEQJo6756EwNGnsK/TSQrWojBB4cQDQ+VaxWYws9tFNDuiMh2Q==} + jasmine-core@5.13.0: + resolution: {integrity: sha512-vsYjfh7lyqvZX5QgqKc4YH8phs7g96Z8bsdIFNEU3VqXhlHaq+vov/Fgn/sr6MiUczdZkyXRC3TX369Ll4Nzbw==} jasmine-spec-reporter@7.0.0: resolution: {integrity: sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==} @@ -3553,6 +3548,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3694,8 +3692,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -3727,8 +3725,8 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memfs@4.51.0: - resolution: {integrity: sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A==} + memfs@4.51.1: + resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==} memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} @@ -3903,8 +3901,8 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + node-forge@1.3.2: + resolution: {integrity: sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: @@ -4234,8 +4232,8 @@ packages: peerDependencies: postcss: ^8.1.0 - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -4253,8 +4251,8 @@ packages: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} - proc-log@6.0.0: - resolution: {integrity: sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} process-nextick-args@2.0.1: @@ -4434,8 +4432,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@1.0.0-beta.51: - resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==} + rolldown@1.0.0-beta.53: + resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -4497,8 +4495,8 @@ packages: webpack: optional: true - sass@1.94.2: - resolution: {integrity: sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==} + sass@1.95.0: + resolution: {integrity: sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==} engines: {node: '>=14.0.0'} hasBin: true @@ -5035,8 +5033,8 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} - vite@7.2.4: - resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5289,117 +5287,117 @@ packages: peerDependencies: zod: ^3.25 || ^4 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} zone.js@0.16.0: resolution: {integrity: sha512-LqLPpIQANebrlxY6jKcYKdgN5DTXyyHAKnnWWjE5pPfEQ4n7j5zn7mOEEpwNZVKGqx3kKKmvplEmoBrvpgROTA==} snapshots: - '@algolia/abtesting@1.11.0': + '@algolia/abtesting@1.12.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-abtesting@5.45.0': + '@algolia/client-abtesting@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-analytics@5.45.0': + '@algolia/client-analytics@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-common@5.45.0': {} + '@algolia/client-common@5.46.0': {} - '@algolia/client-insights@5.45.0': + '@algolia/client-insights@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-personalization@5.45.0': + '@algolia/client-personalization@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-query-suggestions@5.45.0': + '@algolia/client-query-suggestions@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-search@5.45.0': + '@algolia/client-search@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/ingestion@1.45.0': + '@algolia/ingestion@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/monitoring@1.45.0': + '@algolia/monitoring@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/recommend@5.45.0': + '@algolia/recommend@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/requester-browser-xhr@5.45.0': + '@algolia/requester-browser-xhr@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-fetch@5.45.0': + '@algolia/requester-fetch@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-node-http@5.45.0': + '@algolia/requester-node-http@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2101.0-next.0(chokidar@4.0.3)': + '@angular-devkit/architect@0.2101.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(protractor@7.0.0)(typescript@5.9.3)': + '@angular-devkit/build-angular@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(jiti@2.6.1)(protractor@7.0.0)(typescript@5.9.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.0)) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular/build': 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) + '@angular-devkit/architect': 0.2101.0-next.2 + '@angular-devkit/build-webpack': 0.2101.0-next.2(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.1)) + '@angular-devkit/core': 21.1.0-next.2 + '@angular/build': 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) '@angular/compiler-cli': link:in-existing-linked-by-bazel '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -5411,51 +5409,51 @@ snapshots: '@babel/preset-env': 7.28.5(@babel/core@7.28.5) '@babel/runtime': 7.28.4 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + '@ngtools/webpack': 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.22(postcss@8.5.6) - babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)) + babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)) browserslist: 4.28.0 - copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.0)) - css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.0)) - esbuild-wasm: 0.27.0 + copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.1)) + css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.1)) + esbuild-wasm: 0.27.1 http-proxy-middleware: 3.0.5 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 less: 4.4.2 - less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)) - license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.0)) + less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)) + license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.1)) loader-utils: 3.3.1 - mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.0)) + mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.1)) open: 11.0.0 ora: 9.0.0 picomatch: 4.0.3 piscina: 5.1.4 postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) resolve-url-loader: 5.0.0 rxjs: 7.8.2 - sass: 1.94.2 - sass-loader: 16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)) + sass: 1.95.0 + sass-loader: 16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)) semver: 7.7.3 - source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.0)) + source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.1)) source-map-support: 0.5.21 terser: 5.44.1 tinyglobby: 0.2.15 tree-kill: 1.2.2 tslib: 2.8.1 typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-middleware: 7.4.5(webpack@5.103.0) webpack-dev-server: 5.2.2(webpack@5.103.0) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.0)) + webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.1)) optionalDependencies: '@angular/core': link:in-existing-linked-by-bazel '@angular/localize': link:in-existing-linked-by-bazel '@angular/platform-browser': link:in-existing-linked-by-bazel - esbuild: 0.27.0 + esbuild: 0.27.1 protractor: 7.0.0 transitivePeerDependencies: - '@angular/compiler' @@ -5480,16 +5478,16 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.0))': + '@angular-devkit/build-webpack@0.2101.0-next.2(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.1))': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 rxjs: 7.8.2 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-server: 5.2.2(webpack@5.103.0) transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/core@21.1.0-next.2': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -5497,12 +5495,10 @@ snapshots: picomatch: 4.0.3 rxjs: 7.8.2 source-map: 0.7.6 - optionalDependencies: - chokidar: 4.0.3 - '@angular-devkit/schematics@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/schematics@21.1.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.0.0 @@ -5510,20 +5506,20 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular/build@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)': + '@angular/build@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/localize@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 '@angular/compiler': link:in-existing-linked-by-bazel '@angular/compiler-cli': link:in-existing-linked-by-bazel '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 '@inquirer/confirm': 5.1.21(@types/node@20.19.25) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)) beasties: 0.3.5 browserslist: 4.28.0 - esbuild: 0.27.0 + esbuild: 0.27.1 https-proxy-agent: 7.0.6 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 @@ -5533,15 +5529,15 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.3 piscina: 5.1.4 - rolldown: 1.0.0-beta.51 - sass: 1.94.2 + rolldown: 1.0.0-beta.53 + sass: 1.95.0 semver: 7.7.3 source-map-support: 0.5.21 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 undici: 7.16.0 - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1) watchpack: 2.4.4 optionalDependencies: '@angular/core': link:in-existing-linked-by-bazel @@ -5563,17 +5559,17 @@ snapshots: - tsx - yaml - '@angular/cli@21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3)': + '@angular/cli@21.1.0-next.2(@types/node@20.19.25)': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 + '@angular-devkit/core': 21.1.0-next.2 + '@angular-devkit/schematics': 21.1.0-next.2 '@inquirer/prompts': 7.10.1(@types/node@20.19.25) '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.25))(@types/node@20.19.25)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.22.0 - '@schematics/angular': 21.1.0-next.0(chokidar@4.0.3) + '@modelcontextprotocol/sdk': 1.24.3(zod@4.1.13) + '@schematics/angular': 21.1.0-next.2 '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.45.0 + algoliasearch: 5.46.0 ini: 6.0.0 jsonc-parser: 3.3.1 listr2: 9.0.5 @@ -5583,7 +5579,7 @@ snapshots: resolve: 1.22.11 semver: 7.7.3 yargs: 18.0.0 - zod: 3.25.76 + zod: 4.1.13 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' @@ -6285,157 +6281,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.0': + '@esbuild/aix-ppc64@0.27.1': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.0': + '@esbuild/android-arm64@0.27.1': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.0': + '@esbuild/android-arm@0.27.1': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.0': + '@esbuild/android-x64@0.27.1': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.0': + '@esbuild/darwin-arm64@0.27.1': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.0': + '@esbuild/darwin-x64@0.27.1': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.0': + '@esbuild/freebsd-arm64@0.27.1': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.0': + '@esbuild/freebsd-x64@0.27.1': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.0': + '@esbuild/linux-arm64@0.27.1': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.0': + '@esbuild/linux-arm@0.27.1': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.0': + '@esbuild/linux-ia32@0.27.1': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.0': + '@esbuild/linux-loong64@0.27.1': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.0': + '@esbuild/linux-mips64el@0.27.1': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/linux-ppc64@0.27.1': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/linux-riscv64@0.27.1': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/linux-s390x@0.27.1': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/linux-x64@0.27.1': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/netbsd-arm64@0.27.1': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/netbsd-x64@0.27.1': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/openbsd-arm64@0.27.1': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/openbsd-x64@0.27.1': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/openharmony-arm64@0.27.1': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/sunos-x64@0.27.1': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/win32-arm64@0.27.1': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-ia32@0.27.1': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.0': + '@esbuild/win32-x64@0.27.1': optional: true '@inquirer/ansi@1.0.2': {} @@ -6671,7 +6667,7 @@ snapshots: '@lmdb/lmdb-win32-x64@3.4.4': optional: true - '@modelcontextprotocol/sdk@1.22.0': + '@modelcontextprotocol/sdk@1.24.3(zod@4.1.13)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -6682,10 +6678,11 @@ snapshots: eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) + jose: 6.1.3 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod: 4.1.13 + zod-to-json-schema: 3.25.0(zod@4.1.13) transitivePeerDependencies: - supports-color @@ -6779,25 +6776,25 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.0.7': + '@napi-rs/wasm-runtime@1.1.0': dependencies: '@emnapi/core': 1.7.1 '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true - '@ngtools/webpack@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0))': + '@ngtools/webpack@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1))': dependencies: '@angular/compiler-cli': link:in-existing-linked-by-bazel typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.2.2 + lru-cache: 11.2.4 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -6810,9 +6807,9 @@ snapshots: dependencies: '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 npm-pick-manifest: 11.0.3 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 semver: 7.7.3 which: 6.0.0 @@ -6830,7 +6827,7 @@ snapshots: glob: 13.0.0 hosted-git-info: 9.0.2 json-parse-even-better-errors: 5.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 validate-npm-package-license: 3.0.4 @@ -6846,12 +6843,12 @@ snapshots: '@npmcli/package-json': 7.0.4 '@npmcli/promise-spawn': 9.0.1 node-gyp: 12.1.0 - proc-log: 6.0.0 + proc-log: 6.1.0 which: 6.0.0 transitivePeerDependencies: - supports-color - '@oxc-project/types@0.98.0': {} + '@oxc-project/types@0.101.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -6914,51 +6911,48 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true - '@rolldown/binding-android-arm64@1.0.0-beta.51': + '@rolldown/binding-android-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.51': + '@rolldown/binding-darwin-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': dependencies: - '@napi-rs/wasm-runtime': 1.0.7 + '@napi-rs/wasm-runtime': 1.1.0 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': - optional: true - - '@rolldown/pluginutils@1.0.0-beta.51': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} '@rollup/rollup-android-arm-eabi@4.53.3': optional: true @@ -7026,10 +7020,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true - '@schematics/angular@21.1.0-next.0(chokidar@4.0.3)': + '@schematics/angular@21.1.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 + '@angular-devkit/schematics': 21.1.0-next.2 jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -7191,9 +7185,9 @@ snapshots: dependencies: '@types/node': 20.19.25 - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1))': dependencies: - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1) '@webassemblyjs/ast@1.14.1': dependencies: @@ -7348,22 +7342,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.45.0: - dependencies: - '@algolia/abtesting': 1.11.0 - '@algolia/client-abtesting': 5.45.0 - '@algolia/client-analytics': 5.45.0 - '@algolia/client-common': 5.45.0 - '@algolia/client-insights': 5.45.0 - '@algolia/client-personalization': 5.45.0 - '@algolia/client-query-suggestions': 5.45.0 - '@algolia/client-search': 5.45.0 - '@algolia/ingestion': 1.45.0 - '@algolia/monitoring': 1.45.0 - '@algolia/recommend': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + algoliasearch@5.46.0: + dependencies: + '@algolia/abtesting': 1.12.0 + '@algolia/client-abtesting': 5.46.0 + '@algolia/client-analytics': 5.46.0 + '@algolia/client-common': 5.46.0 + '@algolia/client-insights': 5.46.0 + '@algolia/client-personalization': 5.46.0 + '@algolia/client-query-suggestions': 5.46.0 + '@algolia/client-search': 5.46.0 + '@algolia/ingestion': 1.46.0 + '@algolia/monitoring': 1.46.0 + '@algolia/recommend': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 ansi-align@3.0.1: dependencies: @@ -7446,7 +7440,7 @@ snapshots: autoprefixer@10.4.22(postcss@8.5.6): dependencies: browserslist: 4.28.0 - caniuse-lite: 1.0.30001756 + caniuse-lite: 1.0.30001757 fraction.js: 5.3.4 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -7461,11 +7455,11 @@ snapshots: aws4@1.13.2: {} - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)): + babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)): dependencies: '@babel/core': 7.28.5 find-up: 5.0.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: @@ -7493,7 +7487,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.31: {} + baseline-browser-mapping@2.8.32: {} batch@0.6.1: {} @@ -7537,13 +7531,13 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.6.3 + iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.14.0 raw-body: 3.0.2 @@ -7562,7 +7556,7 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.6.2 + chalk: 5.0.1 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0 @@ -7584,9 +7578,9 @@ snapshots: browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.31 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.259 + baseline-browser-mapping: 2.8.32 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.262 node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) @@ -7611,7 +7605,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 minipass-collect: 2.0.1 minipass-flush: 1.0.5 @@ -7643,7 +7637,7 @@ snapshots: camelcase@7.0.1: {} - caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001757: {} caseless@0.12.0: {} @@ -7803,14 +7797,14 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.0)): + copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.1)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 tinyglobby: 0.2.15 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) core-js-compat@3.47.0: dependencies: @@ -7850,7 +7844,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.0)): + css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -7861,7 +7855,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) css-select@6.0.0: dependencies: @@ -7999,7 +7993,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.262: {} emoji-regex@10.6.0: {} @@ -8128,7 +8122,7 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild-wasm@0.27.0: {} + esbuild-wasm@0.27.1: {} esbuild@0.25.12: optionalDependencies: @@ -8159,34 +8153,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.0: + esbuild@0.27.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.0 - '@esbuild/android-arm': 0.27.0 - '@esbuild/android-arm64': 0.27.0 - '@esbuild/android-x64': 0.27.0 - '@esbuild/darwin-arm64': 0.27.0 - '@esbuild/darwin-x64': 0.27.0 - '@esbuild/freebsd-arm64': 0.27.0 - '@esbuild/freebsd-x64': 0.27.0 - '@esbuild/linux-arm': 0.27.0 - '@esbuild/linux-arm64': 0.27.0 - '@esbuild/linux-ia32': 0.27.0 - '@esbuild/linux-loong64': 0.27.0 - '@esbuild/linux-mips64el': 0.27.0 - '@esbuild/linux-ppc64': 0.27.0 - '@esbuild/linux-riscv64': 0.27.0 - '@esbuild/linux-s390x': 0.27.0 - '@esbuild/linux-x64': 0.27.0 - '@esbuild/netbsd-arm64': 0.27.0 - '@esbuild/netbsd-x64': 0.27.0 - '@esbuild/openbsd-arm64': 0.27.0 - '@esbuild/openbsd-x64': 0.27.0 - '@esbuild/openharmony-arm64': 0.27.0 - '@esbuild/sunos-x64': 0.27.0 - '@esbuild/win32-arm64': 0.27.0 - '@esbuild/win32-ia32': 0.27.0 - '@esbuild/win32-x64': 0.27.0 + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 escalade@3.2.0: {} @@ -8282,7 +8276,7 @@ snapshots: express@5.1.0: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 + body-parser: 2.2.1 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 @@ -8540,7 +8534,7 @@ snapshots: hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 hpack.js@2.1.6: dependencies: @@ -8704,7 +8698,7 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.2.0: {} + ipaddr.js@2.3.0: {} is-array-buffer@3.0.5: dependencies: @@ -8904,7 +8898,7 @@ snapshots: jasmine-core@2.8.0: {} - jasmine-core@5.12.1: {} + jasmine-core@5.13.0: {} jasmine-spec-reporter@7.0.0: dependencies: @@ -8926,6 +8920,8 @@ snapshots: jiti@2.6.1: {} + jose@6.1.3: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -8981,11 +8977,11 @@ snapshots: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)): + less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)): dependencies: less: 4.4.2 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) less@4.4.2: dependencies: @@ -9001,11 +8997,11 @@ snapshots: needle: 3.3.1 source-map: 0.6.1 - license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.0)): + license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) lie@3.3.0: dependencies: @@ -9079,7 +9075,7 @@ snapshots: strip-ansi: 7.1.2 wrap-ansi: 9.0.2 - lru-cache@11.2.2: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: @@ -9107,7 +9103,7 @@ snapshots: minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 negotiator: 1.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 ssri: 13.0.0 transitivePeerDependencies: @@ -9119,7 +9115,7 @@ snapshots: media-typer@1.1.0: {} - memfs@4.51.0: + memfs@4.51.1: dependencies: '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) @@ -9167,11 +9163,11 @@ snapshots: mimic-function@5.0.1: {} - mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.0)): + mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.1)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) minimalistic-assert@1.0.1: {} @@ -9277,7 +9273,7 @@ snapshots: node-addon-api@7.1.1: optional: true - node-forge@1.3.1: {} + node-forge@1.3.2: {} node-gyp-build-optional-packages@5.2.2: dependencies: @@ -9291,7 +9287,7 @@ snapshots: graceful-fs: 4.2.11 make-fetch-happen: 15.0.3 nopt: 9.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 tar: 7.5.2 tinyglobby: 0.2.15 @@ -9329,14 +9325,14 @@ snapshots: npm-package-arg@13.0.2: dependencies: hosted-git-info: 9.0.2 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 validate-npm-package-name: 7.0.0 npm-packlist@10.0.3: dependencies: ignore-walk: 8.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 npm-pick-manifest@11.0.3: dependencies: @@ -9354,7 +9350,7 @@ snapshots: minipass-fetch: 5.0.0 minizlib: 3.1.0 npm-package-arg: 13.0.2 - proc-log: 6.0.0 + proc-log: 6.1.0 transitivePeerDependencies: - supports-color @@ -9494,7 +9490,7 @@ snapshots: npm-packlist: 10.0.3 npm-pick-manifest: 11.0.3 npm-registry-fetch: 19.1.1 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 sigstore: 4.0.0 ssri: 13.0.0 @@ -9552,7 +9548,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@0.1.12: {} @@ -9596,14 +9592,14 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - typescript @@ -9617,20 +9613,20 @@ snapshots: dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.1(postcss@8.5.6): dependencies: postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser@7.1.0: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -9647,7 +9643,7 @@ snapshots: proc-log@5.0.0: {} - proc-log@6.0.0: {} + proc-log@6.1.0: {} process-nextick-args@2.0.1: {} @@ -9873,25 +9869,24 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@1.0.0-beta.51: + rolldown@1.0.0-beta.53: dependencies: - '@oxc-project/types': 0.98.0 - '@rolldown/pluginutils': 1.0.0-beta.51 + '@oxc-project/types': 0.101.0 + '@rolldown/pluginutils': 1.0.0-beta.53 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-x64': 1.0.0-beta.51 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.51 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.51 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.51 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.51 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.51 + '@rolldown/binding-android-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-x64': 1.0.0-beta.53 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.53 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.53 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.53 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.53 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53 rollup@4.53.3: dependencies: @@ -9962,14 +9957,14 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)): + sass-loader@16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.94.2 - webpack: 5.103.0(esbuild@0.27.0) + sass: 1.95.0 + webpack: 5.103.0(esbuild@0.27.1) - sass@1.94.2: + sass@1.95.0: dependencies: chokidar: 4.0.3 immutable: 5.1.4 @@ -10004,7 +9999,7 @@ snapshots: selfsigned@2.4.1: dependencies: '@types/node-forge': 1.3.14 - node-forge: 1.3.1 + node-forge: 1.3.2 semver@5.7.2: {} @@ -10225,11 +10220,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.0)): + source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) source-map-support@0.4.18: dependencies: @@ -10415,16 +10410,16 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.103.0): + terser-webpack-plugin@5.3.14(esbuild@0.27.1)(webpack@5.103.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) optionalDependencies: - esbuild: 0.27.0 + esbuild: 0.27.1 terser@5.44.1: dependencies: @@ -10621,7 +10616,7 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1): + vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -10634,7 +10629,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 - sass: 1.94.2 + sass: 1.95.0 terser: 5.44.1 watchpack@2.4.4: @@ -10671,13 +10666,13 @@ snapshots: webpack-dev-middleware@7.4.5(webpack@5.103.0): dependencies: colorette: 2.0.20 - memfs: 4.51.0 + memfs: 4.51.1 mime-types: 3.0.2 on-finished: 2.4.1 range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-server@5.2.2(webpack@5.103.0): dependencies: @@ -10698,7 +10693,7 @@ snapshots: express: 4.21.2 graceful-fs: 4.2.11 http-proxy-middleware: 2.0.9(@types/express@4.17.25) - ipaddr.js: 2.2.0 + ipaddr.js: 2.3.0 launch-editor: 2.12.0 open: 10.2.0 p-retry: 6.2.1 @@ -10710,7 +10705,7 @@ snapshots: webpack-dev-middleware: 7.4.5(webpack@5.103.0) ws: 8.18.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - bufferutil - debug @@ -10725,12 +10720,12 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.0)): + webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) - webpack@5.103.0(esbuild@0.27.0): + webpack@5.103.0(esbuild@0.27.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -10754,7 +10749,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.27.0)(webpack@5.103.0) + terser-webpack-plugin: 5.3.14(esbuild@0.27.1)(webpack@5.103.0) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -10917,10 +10912,10 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.0(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@4.1.13): dependencies: - zod: 3.25.76 + zod: 4.1.13 - zod@3.25.76: {} + zod@4.1.13: {} zone.js@0.16.0: {} diff --git a/integration/cli-hello-world-ivy-i18n/src/app/app.component.html b/integration/cli-hello-world-ivy-i18n/src/app/app.component.html index b181126918e6..d90e78789fc1 100644 --- a/integration/cli-hello-world-ivy-i18n/src/app/app.component.html +++ b/integration/cli-hello-world-ivy-i18n/src/app/app.component.html @@ -1,4 +1,4 @@ -

    Hello {{ title }}!

    +

    Hello {{ title }}!

    {{ locale }}

    {{ 1 | percent }} awesome

    -

    {{ jan | date : 'LLLL' }}

    +

    {{ jan | date: 'LLLL' }}

    diff --git a/integration/cli-hello-world-ivy-i18n/src/index.html b/integration/cli-hello-world-ivy-i18n/src/index.html index 2d837d22b12d..3ebd7f751bbc 100644 --- a/integration/cli-hello-world-ivy-i18n/src/index.html +++ b/integration/cli-hello-world-ivy-i18n/src/index.html @@ -1,14 +1,14 @@ - + - - - CliHelloWorldIvyI18n - + + + CliHelloWorldIvyI18n + - - - - - - + + + + + + diff --git a/integration/cli-hello-world-lazy/package.json b/integration/cli-hello-world-lazy/package.json index f6c957281e40..fe3873adc968 100644 --- a/integration/cli-hello-world-lazy/package.json +++ b/integration/cli-hello-world-lazy/package.json @@ -13,19 +13,19 @@ "@angular/core": "link:./in-existing-linked-by-bazel", "@angular/platform-browser": "link:./in-existing-linked-by-bazel", "@angular/router": "link:./in-existing-linked-by-bazel", - "@angular/ssr": "21.1.0-next.0", + "@angular/ssr": "21.1.0-next.2", "rxjs": "^7.0.0", "tslib": "^2.3.0", "zone.js": "0.16.0" }, "devDependencies": { - "@angular-devkit/build-angular": "21.1.0-next.0", - "@angular/build": "21.1.0-next.0", - "@angular/cli": "21.1.0-next.0", + "@angular-devkit/build-angular": "21.1.0-next.2", + "@angular/build": "21.1.0-next.2", + "@angular/cli": "21.1.0-next.2", "@angular/compiler-cli": "link:./in-existing-linked-by-bazel", "@types/node": "^20.14.8", "ts-node": "^10.9.1", "typescript": "5.9.3" }, - "packageManager": "pnpm@10.23.0" + "packageManager": "pnpm@10.26.0" } diff --git a/integration/cli-hello-world-lazy/pnpm-lock.yaml b/integration/cli-hello-world-lazy/pnpm-lock.yaml index afae97484276..2e263b697d68 100644 --- a/integration/cli-hello-world-lazy/pnpm-lock.yaml +++ b/integration/cli-hello-world-lazy/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: link:./in-existing-linked-by-bazel version: link:in-existing-linked-by-bazel '@angular/ssr': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) rxjs: specifier: ^7.0.0 version: 7.8.2 @@ -37,14 +37,14 @@ importers: version: 0.16.0 devDependencies: '@angular-devkit/build-angular': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(typescript@5.9.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(jiti@2.6.1)(typescript@5.9.3) '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) '@angular/cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@types/node@20.19.25) '@angular/compiler-cli': specifier: link:./in-existing-linked-by-bazel version: link:in-existing-linked-by-bazel @@ -60,72 +60,72 @@ importers: packages: - '@algolia/abtesting@1.11.0': - resolution: {integrity: sha512-a7oQ8dwiyoyVmzLY0FcuBqyqcNSq78qlcOtHmNBumRlHCSnXDcuoYGBGPN1F6n8JoGhviDDsIaF/oQrzTzs6Lg==} + '@algolia/abtesting@1.12.0': + resolution: {integrity: sha512-EfW0bfxjPs+C7ANkJDw2TATntfBKsFiy7APh+KO0pQ8A6HYa5I0NjFuCGCXWfzzzLXNZta3QUl3n5Kmm6aJo9Q==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.45.0': - resolution: {integrity: sha512-WTW0VZA8xHMbzuQD5b3f41ovKZ0MNTIXkWfm0F2PU+XGcLxmxX15UqODzF2sWab0vSbi3URM1xLhJx+bXbd1eQ==} + '@algolia/client-abtesting@5.46.0': + resolution: {integrity: sha512-eG5xV8rujK4ZIHXrRshvv9O13NmU/k42Rnd3w43iKH5RaQ2zWuZO6Q7XjaoJjAFVCsJWqRbXzbYyPGrbF3wGNg==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.45.0': - resolution: {integrity: sha512-I3g7VtvG/QJOH3tQO7E7zWTwBfK/nIQXShFLR8RvPgWburZ626JNj332M3wHCYcaAMivN9WJG66S2JNXhm6+Xg==} + '@algolia/client-analytics@5.46.0': + resolution: {integrity: sha512-AYh2uL8IUW9eZrbbT+wZElyb7QkkeV3US2NEKY7doqMlyPWE8lErNfkVN1NvZdVcY4/SVic5GDbeDz2ft8YIiQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.45.0': - resolution: {integrity: sha512-/nTqm1tLiPtbUr+8kHKyFiCOfhRfgC+JxLvOCq471gFZZOlsh6VtFRiKI60/zGmHTojFC6B0mD80PB7KeK94og==} + '@algolia/client-common@5.46.0': + resolution: {integrity: sha512-0emZTaYOeI9WzJi0TcNd2k3SxiN6DZfdWc2x2gHt855Jl9jPUOzfVTL6gTvCCrOlT4McvpDGg5nGO+9doEjjig==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.45.0': - resolution: {integrity: sha512-suQTx/1bRL1g/K2hRtbK3ANmbzaZCi13487sxxmqok+alBDKKw0/TI73ZiHjjFXM2NV52inwwcmW4fUR45206Q==} + '@algolia/client-insights@5.46.0': + resolution: {integrity: sha512-wrBJ8fE+M0TDG1As4DDmwPn2TXajrvmvAN72Qwpuv8e2JOKNohF7+JxBoF70ZLlvP1A1EiH8DBu+JpfhBbNphQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.45.0': - resolution: {integrity: sha512-CId/dbjpzI3eoUhPU6rt/z4GrRsDesqFISEMOwrqWNSrf4FJhiUIzN42Ac+Gzg69uC0RnzRYy60K1y4Na5VSMw==} + '@algolia/client-personalization@5.46.0': + resolution: {integrity: sha512-LnkeX4p0ENt0DoftDJJDzQQJig/sFQmD1eQifl/iSjhUOGUIKC/7VTeXRcKtQB78naS8njUAwpzFvxy1CDDXDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.45.0': - resolution: {integrity: sha512-tjbBKfA8fjAiFtvl9g/MpIPiD6pf3fj7rirVfh1eMIUi8ybHP4ovDzIaE216vHuRXoePQVCkMd2CokKvYq1CLw==} + '@algolia/client-query-suggestions@5.46.0': + resolution: {integrity: sha512-aF9tc4ex/smypXw+W3lBPB1jjKoaGHpZezTqofvDOI/oK1dR2sdTpFpK2Ru+7IRzYgwtRqHF3znmTlyoNs9dpA==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.45.0': - resolution: {integrity: sha512-nxuCid+Nszs4xqwIMDw11pRJPes2c+Th1yup/+LtpjFH8QWXkr3SirNYSD3OXAeM060HgWWPLA8/Fxk+vwxQOA==} + '@algolia/client-search@5.46.0': + resolution: {integrity: sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.45.0': - resolution: {integrity: sha512-t+1doBzhkQTeOOjLHMlm4slmXBhvgtEGQhOmNpMPTnIgWOyZyESWdm+XD984qM4Ej1i9FRh8VttOGrdGnAjAng==} + '@algolia/ingestion@1.46.0': + resolution: {integrity: sha512-2LT0/Z+/sFwEpZLH6V17WSZ81JX2uPjgvv5eNlxgU7rPyup4NXXfuMbtCJ+6uc4RO/LQpEJd3Li59ke3wtyAsA==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.45.0': - resolution: {integrity: sha512-IaX3ZX1A/0wlgWZue+1BNWlq5xtJgsRo7uUk/aSiYD7lPbJ7dFuZ+yTLFLKgbl4O0QcyHTj1/mSBj9ryF1Lizg==} + '@algolia/monitoring@1.46.0': + resolution: {integrity: sha512-uivZ9wSWZ8mz2ZU0dgDvQwvVZV8XBv6lYBXf8UtkQF3u7WeTqBPeU8ZoeTyLpf0jAXCYOvc1mAVmK0xPLuEwOQ==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.45.0': - resolution: {integrity: sha512-1jeMLoOhkgezCCPsOqkScwYzAAc1Jr5T2hisZl0s32D94ZV7d1OHozBukgOjf8Dw+6Hgi6j52jlAdUWTtkX9Mg==} + '@algolia/recommend@5.46.0': + resolution: {integrity: sha512-O2BB8DuySuddgOAbhyH4jsGbL+KyDGpzJRtkDZkv091OMomqIA78emhhMhX9d/nIRrzS1wNLWB/ix7Hb2eV5rg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.45.0': - resolution: {integrity: sha512-46FIoUkQ9N7wq4/YkHS5/W9Yjm4Ab+q5kfbahdyMpkBPJ7IBlwuNEGnWUZIQ6JfUZuJVojRujPRHMihX4awUMg==} + '@algolia/requester-browser-xhr@5.46.0': + resolution: {integrity: sha512-eW6xyHCyYrJD0Kjk9Mz33gQ40LfWiEA51JJTVfJy3yeoRSw/NXhAL81Pljpa0qslTs6+LO/5DYPZddct6HvISQ==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.45.0': - resolution: {integrity: sha512-XFTSAtCwy4HdBhSReN2rhSyH/nZOM3q3qe5ERG2FLbYId62heIlJBGVyAPRbltRwNlotlydbvSJ+SQ0ruWC2cw==} + '@algolia/requester-fetch@5.46.0': + resolution: {integrity: sha512-Vn2+TukMGHy4PIxmdvP667tN/MhS7MPT8EEvEhS6JyFLPx3weLcxSa1F9gVvrfHWCUJhLWoMVJVB2PT8YfRGcw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.45.0': - resolution: {integrity: sha512-8mTg6lHx5i44raCU52APsu0EqMsdm4+7Hch/e4ZsYZw0hzwkuaMFh826ngnkYf9XOl58nHoou63aZ874m8AbpQ==} + '@algolia/requester-node-http@5.46.0': + resolution: {integrity: sha512-xaqXyna5yBZ+r1SJ9my/DM6vfTqJg9FJgVydRJ0lnO+D5NhqGW/qaRG/iBGKr/d4fho34el6WakV7BqJvrl/HQ==} engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2101.0-next.0': - resolution: {integrity: sha512-aExU6NQA1RDKqH9oecf+YBSZE7UlsdsWKqMJ8Me8Zj7PzMy6CNu82xL1Cv4yRB3xTHfNlB+CSnwnU6FoOoGohw==} + '@angular-devkit/architect@0.2101.0-next.2': + resolution: {integrity: sha512-Lm8VsC+FmZ29/iZ4fjyAqp+kFjzY+UPUtIAFcckux0ss63JxOWxBhgU3QL8EwvGCnaUuYO7XoBwG3fV9dNp0fA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/build-angular@21.1.0-next.0': - resolution: {integrity: sha512-WT1Bzm0KLS+5me7Cup5o0iM6JaPjU9yP8S4AVX54ikHEtuiw+dxiait7aEAapMlcO8Wt2m8FMC+X0RChReAtJA==} + '@angular-devkit/build-angular@21.1.0-next.2': + resolution: {integrity: sha512-fZvBzacz/NpSRvN0eSRzZXm5XIuZtd/FLosjsqcNcVM05pK1SxC7foHoW1nOQ9S4W4lq9inLpD4VNN4AZgH04A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -134,7 +134,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 '@web/test-runner': ^0.20.0 browser-sync: ^3.0.2 jest: ^30.2.0 @@ -174,28 +174,28 @@ packages: tailwindcss: optional: true - '@angular-devkit/build-webpack@0.2101.0-next.0': - resolution: {integrity: sha512-eLnDxsc3YfdLjl+0FPPunEjvmwdhmH28aiZIQxqN0ULH/iKercdjuI51ilwG/fS+UUpIxazXcyHqv9MjL0Sc/Q==} + '@angular-devkit/build-webpack@0.2101.0-next.2': + resolution: {integrity: sha512-lj2zbp9Xo2qPm3qGRswYsOzZ8ED9HExMTHt9pDDzRcVpnKCqQGSINmJVndnD8xLVt92S39Cpr1QBOsE6U9FKXQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - '@angular-devkit/core@21.1.0-next.0': - resolution: {integrity: sha512-a48yNgGp79R23HPekTjjcwgRrtJEDndN1nBs0j42qgg+jSM/cqeTw7rMjNPAgNv9dkqxn3Rk/G1WGKIEHANlkQ==} + '@angular-devkit/core@21.1.0-next.2': + resolution: {integrity: sha512-SavlqKJmM+EqX1eg4iuboaqbJTBRALuwuocctSBmowOoYFYjJXaJbdit1fwB0vqQ946RKG56YYCcAEuIhjmNwA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - chokidar: ^4.0.0 + chokidar: ^5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@21.1.0-next.0': - resolution: {integrity: sha512-fi8zVvjGkrW3DQTB8NEHvKVQpW62B1o3ZMazRYRTwxMlqJiWn+eCU3EzuVw1ScgxlvnHeacesxIn9OPovGFIug==} + '@angular-devkit/schematics@21.1.0-next.2': + resolution: {integrity: sha512-01krelZX9UC8l8BlkIpExQ8fIekvtnSk0831j9dbWxbhKu/QZa8IHbKsB14Ph6NNvlz84FmUFhZpODMwjS831w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular/build@21.1.0-next.0': - resolution: {integrity: sha512-2vH+9Caw75JnviZ97mibOLPaTwJgHxjVuYoE9za5Arm9Up4NsCakui//V379qkLobsEAnln0huh8g0PC+mJ4Jg==} + '@angular/build@21.1.0-next.2': + resolution: {integrity: sha512-WHbkVNH5t+OyOd3ciWKxMhAe9IdTGMqUxH+HasiJ+MYcwQ4SCqL4GWsOPl+UiJrOUKa4At9yd5lnOpjz1kWrWA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^21.0.0 || ^21.1.0-next.0 @@ -205,7 +205,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 || ^21.1.0-next.0 @@ -240,13 +240,13 @@ packages: vitest: optional: true - '@angular/cli@21.1.0-next.0': - resolution: {integrity: sha512-QpboTD5qCFjw1SRPjnVWJQ7c2s4Co5rnHSvVWuAixtM4rlNkulzgzcAndO4HqjnBq4Q8RRs8V1L/JeW71MSfwQ==} + '@angular/cli@21.1.0-next.2': + resolution: {integrity: sha512-SLBX5G187ih0gu+SoXoQ6wPJcmbZtVwjD705YEPH4YD1vsv1IP8YOwGlIV5SHbv7A4cM+wL1S1HNHD/e950iJQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/ssr@21.1.0-next.0': - resolution: {integrity: sha512-8sit1MbJq0wc/VgSs7Rd2yRUiLV/WFGi3vZgJ9A07s9nuRy/asB4OHgxVb3avINlGp+XaifbUONWRqy65XbwZg==} + '@angular/ssr@21.1.0-next.2': + resolution: {integrity: sha512-WYORIbnS5wkp/sVosb4dsmu96lg/x2hdKE8oBag5/3RfLp2xjc1W0wY3XAAF+YJyiKIJG24X487zwiArzq0B9w==} peerDependencies: '@angular/common': ^21.0.0 || ^21.1.0-next.0 '@angular/core': ^21.0.0 || ^21.1.0-next.0 @@ -784,8 +784,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -796,8 +796,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -808,8 +808,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -820,8 +820,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -832,8 +832,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -844,8 +844,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -856,8 +856,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': - resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -868,8 +868,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -880,8 +880,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -892,8 +892,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -904,8 +904,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -916,8 +916,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -928,8 +928,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -940,8 +940,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -952,8 +952,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.0': - resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -964,8 +964,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -976,8 +976,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -988,8 +988,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1000,8 +1000,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.0': - resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1012,8 +1012,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1024,8 +1024,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.0': - resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1036,8 +1036,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': - resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1048,8 +1048,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1060,8 +1060,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1072,8 +1072,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1084,8 +1084,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1343,11 +1343,12 @@ packages: cpu: [x64] os: [win32] - '@modelcontextprotocol/sdk@1.22.0': - resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} + '@modelcontextprotocol/sdk@1.24.3': + resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -1495,11 +1496,11 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} - '@ngtools/webpack@21.1.0-next.0': - resolution: {integrity: sha512-lFlrmQARJJKwlYfoGGXKrr7Bbk5FJzx6xCEgw6ZqnH5FjIaxHrXZ+jLmoEeqUKeX0NSuX5q0oLpE40IExFh2FA==} + '@ngtools/webpack@21.1.0-next.2': + resolution: {integrity: sha512-EFou/qczcYYaKFunyl4iLp6tJqClwO2lngesO6eDRlV2LdRNlB0uImmurNyluaM3vKkLvRkRVlzq0G4Q6kQTUw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -1543,8 +1544,8 @@ packages: resolution: {integrity: sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==} engines: {node: ^20.17.0 || >=22.9.0} - '@oxc-project/types@0.98.0': - resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==} + '@oxc-project/types@0.101.0': + resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==} '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} @@ -1634,95 +1635,89 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} - '@rolldown/binding-android-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==} + '@rolldown/binding-android-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.51': - resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==} + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': - resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': - resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': - resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.51': - resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==} + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/rollup-android-arm-eabi@4.53.3': resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} @@ -1845,8 +1840,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@21.1.0-next.0': - resolution: {integrity: sha512-TBSh8b8+OdlgWkJlTTv/JRwrPTRH+mpJ32AlKRBimDChPDVP/+MAKlF7tsSbXKsi+V9mVWuBXcVR1A0R87mjkw==} + '@schematics/angular@21.1.0-next.2': + resolution: {integrity: sha512-NqVq5MbwNhJ5Phmv1pzj8ZvIVoGi3e4a702VbBlS6KxoZI9j8ulBh8ZnAWN3q6ZkSRnOIuPqvgowfeNbRtplFg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': @@ -2087,8 +2082,8 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@5.45.0: - resolution: {integrity: sha512-wrj4FGr14heLOYkBKV3Fbq5ZBGuIFeDJkTilYq/G+hH1CSlQBtYvG2X1j67flwv0fUeQJwnWxxRIunSemAZirA==} + algoliasearch@5.46.0: + resolution: {integrity: sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==} engines: {node: '>= 14.0.0'} ansi-colors@4.1.3: @@ -2165,8 +2160,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.31: - resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} + baseline-browser-mapping@2.8.32: + resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==} hasBin: true batch@0.6.1: @@ -2187,8 +2182,8 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} bonjour-service@1.3.0: @@ -2236,8 +2231,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} @@ -2484,8 +2479,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.259: - resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.262: + resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2553,8 +2548,8 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild-wasm@0.27.0: - resolution: {integrity: sha512-4XpLDOY4sOzqgiezPXDkHTn25cBA1MKN5OTotehoFR7/wTpSYCK76OA8rQfpiuz12G27JpGxxdh+/D9GcNl61w==} + esbuild-wasm@0.27.1: + resolution: {integrity: sha512-NjueSyuuMjX6F/mnqQ8g4rkwAFTct7JT/A/oXjXhGTFbWiTm3zU59BMulOrT+KuCHj+oShTBARQCxCDDvVxwFA==} engines: {node: '>=18'} hasBin: true @@ -2563,8 +2558,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} engines: {node: '>=18'} hasBin: true @@ -2888,8 +2883,8 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} is-arrayish@0.2.1: @@ -3001,6 +2996,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3109,8 +3107,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -3142,8 +3140,8 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memfs@4.51.0: - resolution: {integrity: sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A==} + memfs@4.51.1: + resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==} merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -3293,8 +3291,8 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + node-forge@1.3.2: + resolution: {integrity: sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: @@ -3524,8 +3522,8 @@ packages: peerDependencies: postcss: ^8.1.0 - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -3543,8 +3541,8 @@ packages: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} - proc-log@6.0.0: - resolution: {integrity: sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} process-nextick-args@2.0.1: @@ -3655,8 +3653,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.0-beta.51: - resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==} + rolldown@1.0.0-beta.53: + resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -3706,8 +3704,8 @@ packages: webpack: optional: true - sass@1.94.2: - resolution: {integrity: sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==} + sass@1.95.0: + resolution: {integrity: sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==} engines: {node: '>=14.0.0'} hasBin: true @@ -4083,8 +4081,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@7.2.4: - resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4278,117 +4276,117 @@ packages: peerDependencies: zod: ^3.25 || ^4 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} zone.js@0.16.0: resolution: {integrity: sha512-LqLPpIQANebrlxY6jKcYKdgN5DTXyyHAKnnWWjE5pPfEQ4n7j5zn7mOEEpwNZVKGqx3kKKmvplEmoBrvpgROTA==} snapshots: - '@algolia/abtesting@1.11.0': + '@algolia/abtesting@1.12.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-abtesting@5.45.0': + '@algolia/client-abtesting@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-analytics@5.45.0': + '@algolia/client-analytics@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-common@5.45.0': {} + '@algolia/client-common@5.46.0': {} - '@algolia/client-insights@5.45.0': + '@algolia/client-insights@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-personalization@5.45.0': + '@algolia/client-personalization@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-query-suggestions@5.45.0': + '@algolia/client-query-suggestions@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-search@5.45.0': + '@algolia/client-search@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/ingestion@1.45.0': + '@algolia/ingestion@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/monitoring@1.45.0': + '@algolia/monitoring@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/recommend@5.45.0': + '@algolia/recommend@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/requester-browser-xhr@5.45.0': + '@algolia/requester-browser-xhr@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-fetch@5.45.0': + '@algolia/requester-fetch@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-node-http@5.45.0': + '@algolia/requester-node-http@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2101.0-next.0(chokidar@4.0.3)': + '@angular-devkit/architect@0.2101.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(typescript@5.9.3)': + '@angular-devkit/build-angular@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(jiti@2.6.1)(typescript@5.9.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.0)) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular/build': 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) + '@angular-devkit/architect': 0.2101.0-next.2 + '@angular-devkit/build-webpack': 0.2101.0-next.2(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.1)) + '@angular-devkit/core': 21.1.0-next.2 + '@angular/build': 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3) '@angular/compiler-cli': link:in-existing-linked-by-bazel '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -4400,51 +4398,51 @@ snapshots: '@babel/preset-env': 7.28.5(@babel/core@7.28.5) '@babel/runtime': 7.28.4 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + '@ngtools/webpack': 21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.22(postcss@8.5.6) - babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)) + babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)) browserslist: 4.28.0 - copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.0)) - css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.0)) - esbuild-wasm: 0.27.0 + copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.1)) + css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.1)) + esbuild-wasm: 0.27.1 http-proxy-middleware: 3.0.5 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 less: 4.4.2 - less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)) - license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.0)) + less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)) + license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.1)) loader-utils: 3.3.1 - mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.0)) + mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.1)) open: 11.0.0 ora: 9.0.0 picomatch: 4.0.3 piscina: 5.1.4 postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) resolve-url-loader: 5.0.0 rxjs: 7.8.2 - sass: 1.94.2 - sass-loader: 16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)) + sass: 1.95.0 + sass-loader: 16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)) semver: 7.7.3 - source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.0)) + source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.1)) source-map-support: 0.5.21 terser: 5.44.1 tinyglobby: 0.2.15 tree-kill: 1.2.2 tslib: 2.8.1 typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-middleware: 7.4.5(webpack@5.103.0) webpack-dev-server: 5.2.2(webpack@5.103.0) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.0)) + webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.1)) optionalDependencies: '@angular/core': link:in-existing-linked-by-bazel '@angular/platform-browser': link:in-existing-linked-by-bazel - '@angular/ssr': 21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) - esbuild: 0.27.0 + '@angular/ssr': 21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) + esbuild: 0.27.1 transitivePeerDependencies: - '@angular/compiler' - '@rspack/core' @@ -4468,16 +4466,16 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.0))': + '@angular-devkit/build-webpack@0.2101.0-next.2(webpack-dev-server@5.2.2(webpack@5.103.0))(webpack@5.103.0(esbuild@0.27.1))': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 rxjs: 7.8.2 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-server: 5.2.2(webpack@5.103.0) transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/core@21.1.0-next.2': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -4485,12 +4483,10 @@ snapshots: picomatch: 4.0.3 rxjs: 7.8.2 source-map: 0.7.6 - optionalDependencies: - chokidar: 4.0.3 - '@angular-devkit/schematics@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/schematics@21.1.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.0.0 @@ -4498,20 +4494,20 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular/build@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)': + '@angular/build@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(@angular/compiler@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/platform-browser@in-existing-linked-by-bazel)(@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel))(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 '@angular/compiler': link:in-existing-linked-by-bazel '@angular/compiler-cli': link:in-existing-linked-by-bazel '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 '@inquirer/confirm': 5.1.21(@types/node@20.19.25) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)) beasties: 0.3.5 browserslist: 4.28.0 - esbuild: 0.27.0 + esbuild: 0.27.1 https-proxy-agent: 7.0.6 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 @@ -4521,20 +4517,20 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.3 piscina: 5.1.4 - rolldown: 1.0.0-beta.51 - sass: 1.94.2 + rolldown: 1.0.0-beta.53 + sass: 1.95.0 semver: 7.7.3 source-map-support: 0.5.21 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 undici: 7.16.0 - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1) watchpack: 2.4.4 optionalDependencies: '@angular/core': link:in-existing-linked-by-bazel '@angular/platform-browser': link:in-existing-linked-by-bazel - '@angular/ssr': 21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) + '@angular/ssr': 21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel) less: 4.4.2 lmdb: 3.4.4 postcss: 8.5.6 @@ -4551,17 +4547,17 @@ snapshots: - tsx - yaml - '@angular/cli@21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3)': + '@angular/cli@21.1.0-next.2(@types/node@20.19.25)': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2 + '@angular-devkit/core': 21.1.0-next.2 + '@angular-devkit/schematics': 21.1.0-next.2 '@inquirer/prompts': 7.10.1(@types/node@20.19.25) '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.25))(@types/node@20.19.25)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.22.0 - '@schematics/angular': 21.1.0-next.0(chokidar@4.0.3) + '@modelcontextprotocol/sdk': 1.24.3(zod@4.1.13) + '@schematics/angular': 21.1.0-next.2 '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.45.0 + algoliasearch: 5.46.0 ini: 6.0.0 jsonc-parser: 3.3.1 listr2: 9.0.5 @@ -4571,14 +4567,14 @@ snapshots: resolve: 1.22.11 semver: 7.7.3 yargs: 18.0.0 - zod: 3.25.76 + zod: 4.1.13 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/ssr@21.1.0-next.0(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel)': + '@angular/ssr@21.1.0-next.2(@angular/common@in-existing-linked-by-bazel)(@angular/core@in-existing-linked-by-bazel)(@angular/router@in-existing-linked-by-bazel)': dependencies: '@angular/common': link:in-existing-linked-by-bazel '@angular/core': link:in-existing-linked-by-bazel @@ -5280,157 +5276,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.0': + '@esbuild/aix-ppc64@0.27.1': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.0': + '@esbuild/android-arm64@0.27.1': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.0': + '@esbuild/android-arm@0.27.1': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.0': + '@esbuild/android-x64@0.27.1': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.0': + '@esbuild/darwin-arm64@0.27.1': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.0': + '@esbuild/darwin-x64@0.27.1': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.0': + '@esbuild/freebsd-arm64@0.27.1': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.0': + '@esbuild/freebsd-x64@0.27.1': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.0': + '@esbuild/linux-arm64@0.27.1': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.0': + '@esbuild/linux-arm@0.27.1': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.0': + '@esbuild/linux-ia32@0.27.1': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.0': + '@esbuild/linux-loong64@0.27.1': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.0': + '@esbuild/linux-mips64el@0.27.1': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/linux-ppc64@0.27.1': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/linux-riscv64@0.27.1': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/linux-s390x@0.27.1': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/linux-x64@0.27.1': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/netbsd-arm64@0.27.1': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/netbsd-x64@0.27.1': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/openbsd-arm64@0.27.1': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/openbsd-x64@0.27.1': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/openharmony-arm64@0.27.1': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/sunos-x64@0.27.1': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/win32-arm64@0.27.1': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-ia32@0.27.1': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.0': + '@esbuild/win32-x64@0.27.1': optional: true '@inquirer/ansi@1.0.2': {} @@ -5666,7 +5662,7 @@ snapshots: '@lmdb/lmdb-win32-x64@3.4.4': optional: true - '@modelcontextprotocol/sdk@1.22.0': + '@modelcontextprotocol/sdk@1.24.3(zod@4.1.13)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -5677,10 +5673,11 @@ snapshots: eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) + jose: 6.1.3 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod: 4.1.13 + zod-to-json-schema: 3.25.0(zod@4.1.13) transitivePeerDependencies: - supports-color @@ -5774,25 +5771,25 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.0.7': + '@napi-rs/wasm-runtime@1.1.0': dependencies: '@emnapi/core': 1.7.1 '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true - '@ngtools/webpack@21.1.0-next.0(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0))': + '@ngtools/webpack@21.1.0-next.2(@angular/compiler-cli@in-existing-linked-by-bazel)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1))': dependencies: '@angular/compiler-cli': link:in-existing-linked-by-bazel typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.2.2 + lru-cache: 11.2.4 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -5805,9 +5802,9 @@ snapshots: dependencies: '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 npm-pick-manifest: 11.0.3 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 semver: 7.7.3 which: 6.0.0 @@ -5825,7 +5822,7 @@ snapshots: glob: 13.0.0 hosted-git-info: 9.0.2 json-parse-even-better-errors: 5.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 validate-npm-package-license: 3.0.4 @@ -5841,12 +5838,12 @@ snapshots: '@npmcli/package-json': 7.0.4 '@npmcli/promise-spawn': 9.0.1 node-gyp: 12.1.0 - proc-log: 6.0.0 + proc-log: 6.1.0 which: 6.0.0 transitivePeerDependencies: - supports-color - '@oxc-project/types@0.98.0': {} + '@oxc-project/types@0.101.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -5909,51 +5906,48 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true - '@rolldown/binding-android-arm64@1.0.0-beta.51': + '@rolldown/binding-android-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.51': + '@rolldown/binding-darwin-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': + '@napi-rs/wasm-runtime': 1.1.0 optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': optional: true - '@rolldown/pluginutils@1.0.0-beta.51': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} '@rollup/rollup-android-arm-eabi@4.53.3': optional: true @@ -6021,10 +6015,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true - '@schematics/angular@21.1.0-next.0(chokidar@4.0.3)': + '@schematics/angular@21.1.0-next.2': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2 + '@angular-devkit/schematics': 21.1.0-next.2 jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -6176,9 +6170,9 @@ snapshots: dependencies: '@types/node': 20.19.25 - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1))': dependencies: - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1) '@webassemblyjs/ast@1.14.1': dependencies: @@ -6311,22 +6305,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.45.0: - dependencies: - '@algolia/abtesting': 1.11.0 - '@algolia/client-abtesting': 5.45.0 - '@algolia/client-analytics': 5.45.0 - '@algolia/client-common': 5.45.0 - '@algolia/client-insights': 5.45.0 - '@algolia/client-personalization': 5.45.0 - '@algolia/client-query-suggestions': 5.45.0 - '@algolia/client-search': 5.45.0 - '@algolia/ingestion': 1.45.0 - '@algolia/monitoring': 1.45.0 - '@algolia/recommend': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + algoliasearch@5.46.0: + dependencies: + '@algolia/abtesting': 1.12.0 + '@algolia/client-abtesting': 5.46.0 + '@algolia/client-analytics': 5.46.0 + '@algolia/client-common': 5.46.0 + '@algolia/client-insights': 5.46.0 + '@algolia/client-personalization': 5.46.0 + '@algolia/client-query-suggestions': 5.46.0 + '@algolia/client-search': 5.46.0 + '@algolia/ingestion': 1.46.0 + '@algolia/monitoring': 1.46.0 + '@algolia/recommend': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 ansi-colors@4.1.3: {} @@ -6360,18 +6354,18 @@ snapshots: autoprefixer@10.4.22(postcss@8.5.6): dependencies: browserslist: 4.28.0 - caniuse-lite: 1.0.30001756 + caniuse-lite: 1.0.30001757 fraction.js: 5.3.4 normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)): + babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)): dependencies: '@babel/core': 7.28.5 find-up: 5.0.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: @@ -6399,7 +6393,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.31: {} + baseline-browser-mapping@2.8.32: {} batch@0.6.1: {} @@ -6435,13 +6429,13 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.6.3 + iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.14.0 raw-body: 3.0.2 @@ -6466,9 +6460,9 @@ snapshots: browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.31 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.259 + baseline-browser-mapping: 2.8.32 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.262 node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) @@ -6485,7 +6479,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 minipass-collect: 2.0.1 minipass-flush: 1.0.5 @@ -6506,7 +6500,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001757: {} chalk@5.6.2: {} @@ -6609,14 +6603,14 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.0)): + copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.1)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 tinyglobby: 0.2.15 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) core-js-compat@3.47.0: dependencies: @@ -6646,7 +6640,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.0)): + css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -6657,7 +6651,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) css-select@6.0.0: dependencies: @@ -6734,7 +6728,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.262: {} emoji-regex@10.6.0: {} @@ -6785,7 +6779,7 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild-wasm@0.27.0: {} + esbuild-wasm@0.27.1: {} esbuild@0.25.12: optionalDependencies: @@ -6816,34 +6810,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.0: + esbuild@0.27.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.0 - '@esbuild/android-arm': 0.27.0 - '@esbuild/android-arm64': 0.27.0 - '@esbuild/android-x64': 0.27.0 - '@esbuild/darwin-arm64': 0.27.0 - '@esbuild/darwin-x64': 0.27.0 - '@esbuild/freebsd-arm64': 0.27.0 - '@esbuild/freebsd-x64': 0.27.0 - '@esbuild/linux-arm': 0.27.0 - '@esbuild/linux-arm64': 0.27.0 - '@esbuild/linux-ia32': 0.27.0 - '@esbuild/linux-loong64': 0.27.0 - '@esbuild/linux-mips64el': 0.27.0 - '@esbuild/linux-ppc64': 0.27.0 - '@esbuild/linux-riscv64': 0.27.0 - '@esbuild/linux-s390x': 0.27.0 - '@esbuild/linux-x64': 0.27.0 - '@esbuild/netbsd-arm64': 0.27.0 - '@esbuild/netbsd-x64': 0.27.0 - '@esbuild/openbsd-arm64': 0.27.0 - '@esbuild/openbsd-x64': 0.27.0 - '@esbuild/openharmony-arm64': 0.27.0 - '@esbuild/sunos-x64': 0.27.0 - '@esbuild/win32-arm64': 0.27.0 - '@esbuild/win32-ia32': 0.27.0 - '@esbuild/win32-x64': 0.27.0 + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 escalade@3.2.0: {} @@ -6923,7 +6917,7 @@ snapshots: express@5.1.0: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 + body-parser: 2.2.1 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 @@ -7079,7 +7073,7 @@ snapshots: hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 hpack.js@2.1.6: dependencies: @@ -7213,7 +7207,7 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.2.0: {} + ipaddr.js@2.3.0: {} is-arrayish@0.2.1: {} @@ -7297,6 +7291,8 @@ snapshots: jiti@2.6.1: {} + jose@6.1.3: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -7328,11 +7324,11 @@ snapshots: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)): + less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)): dependencies: less: 4.4.2 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) less@4.4.2: dependencies: @@ -7348,11 +7344,11 @@ snapshots: needle: 3.3.1 source-map: 0.6.1 - license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.0)): + license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) lines-and-columns@1.2.4: {} @@ -7411,7 +7407,7 @@ snapshots: strip-ansi: 7.1.2 wrap-ansi: 9.0.2 - lru-cache@11.2.2: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: @@ -7439,7 +7435,7 @@ snapshots: minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 negotiator: 1.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 ssri: 13.0.0 transitivePeerDependencies: @@ -7451,7 +7447,7 @@ snapshots: media-typer@1.1.0: {} - memfs@4.51.0: + memfs@4.51.1: dependencies: '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) @@ -7489,11 +7485,11 @@ snapshots: mimic-function@5.0.1: {} - mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.0)): + mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.1)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) minimalistic-assert@1.0.1: {} @@ -7591,7 +7587,7 @@ snapshots: node-addon-api@7.1.1: optional: true - node-forge@1.3.1: {} + node-forge@1.3.2: {} node-gyp-build-optional-packages@5.2.2: dependencies: @@ -7605,7 +7601,7 @@ snapshots: graceful-fs: 4.2.11 make-fetch-happen: 15.0.3 nopt: 9.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 tar: 7.5.2 tinyglobby: 0.2.15 @@ -7636,14 +7632,14 @@ snapshots: npm-package-arg@13.0.2: dependencies: hosted-git-info: 9.0.2 - proc-log: 6.0.0 + proc-log: 6.1.0 semver: 7.7.3 validate-npm-package-name: 7.0.0 npm-packlist@10.0.3: dependencies: ignore-walk: 8.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 npm-pick-manifest@11.0.3: dependencies: @@ -7661,7 +7657,7 @@ snapshots: minipass-fetch: 5.0.0 minizlib: 3.1.0 npm-package-arg: 13.0.2 - proc-log: 6.0.0 + proc-log: 6.1.0 transitivePeerDependencies: - supports-color @@ -7750,7 +7746,7 @@ snapshots: npm-packlist: 10.0.3 npm-pick-manifest: 11.0.3 npm-registry-fetch: 19.1.1 - proc-log: 6.0.0 + proc-log: 6.1.0 promise-retry: 2.0.1 sigstore: 4.0.0 ssri: 13.0.0 @@ -7795,7 +7791,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@0.1.12: {} @@ -7817,14 +7813,14 @@ snapshots: pkce-challenge@5.0.1: {} - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - typescript @@ -7838,20 +7834,20 @@ snapshots: dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.1(postcss@8.5.6): dependencies: postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser@7.1.0: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -7868,7 +7864,7 @@ snapshots: proc-log@5.0.0: {} - proc-log@6.0.0: {} + proc-log@6.1.0: {} process-nextick-args@2.0.1: {} @@ -7989,25 +7985,24 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.0-beta.51: + rolldown@1.0.0-beta.53: dependencies: - '@oxc-project/types': 0.98.0 - '@rolldown/pluginutils': 1.0.0-beta.51 + '@oxc-project/types': 0.101.0 + '@rolldown/pluginutils': 1.0.0-beta.53 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-x64': 1.0.0-beta.51 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.51 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.51 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.51 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.51 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.51 + '@rolldown/binding-android-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-x64': 1.0.0-beta.53 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.53 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.53 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.53 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.53 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53 rollup@4.53.3: dependencies: @@ -8059,14 +8054,14 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)): + sass-loader@16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.94.2 - webpack: 5.103.0(esbuild@0.27.0) + sass: 1.95.0 + webpack: 5.103.0(esbuild@0.27.1) - sass@1.94.2: + sass@1.95.0: dependencies: chokidar: 4.0.3 immutable: 5.1.4 @@ -8089,7 +8084,7 @@ snapshots: selfsigned@2.4.1: dependencies: '@types/node-forge': 1.3.14 - node-forge: 1.3.1 + node-forge: 1.3.2 semver@5.7.2: optional: true @@ -8251,11 +8246,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.0)): + source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) source-map-support@0.5.21: dependencies: @@ -8362,16 +8357,16 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.103.0): + terser-webpack-plugin@5.3.14(esbuild@0.27.1)(webpack@5.103.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) optionalDependencies: - esbuild: 0.27.0 + esbuild: 0.27.1 terser@5.44.1: dependencies: @@ -8494,7 +8489,7 @@ snapshots: vary@1.1.2: {} - vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1): + vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -8507,7 +8502,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 - sass: 1.94.2 + sass: 1.95.0 terser: 5.44.1 watchpack@2.4.4: @@ -8525,13 +8520,13 @@ snapshots: webpack-dev-middleware@7.4.5(webpack@5.103.0): dependencies: colorette: 2.0.20 - memfs: 4.51.0 + memfs: 4.51.1 mime-types: 3.0.2 on-finished: 2.4.1 range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) webpack-dev-server@5.2.2(webpack@5.103.0): dependencies: @@ -8552,7 +8547,7 @@ snapshots: express: 4.21.2 graceful-fs: 4.2.11 http-proxy-middleware: 2.0.9(@types/express@4.17.25) - ipaddr.js: 2.2.0 + ipaddr.js: 2.3.0 launch-editor: 2.12.0 open: 10.2.0 p-retry: 6.2.1 @@ -8564,7 +8559,7 @@ snapshots: webpack-dev-middleware: 7.4.5(webpack@5.103.0) ws: 8.18.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - bufferutil - debug @@ -8579,12 +8574,12 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.0)): + webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) - webpack@5.103.0(esbuild@0.27.0): + webpack@5.103.0(esbuild@0.27.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -8608,7 +8603,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.27.0)(webpack@5.103.0) + terser-webpack-plugin: 5.3.14(esbuild@0.27.1)(webpack@5.103.0) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -8686,10 +8681,10 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.0(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@4.1.13): dependencies: - zod: 3.25.76 + zod: 4.1.13 - zod@3.25.76: {} + zod@4.1.13: {} zone.js@0.16.0: {} diff --git a/integration/cli-hello-world-lazy/src/app/app.component.html b/integration/cli-hello-world-lazy/src/app/app.component.html index fe317893dd68..8769fe9f70d1 100644 --- a/integration/cli-hello-world-lazy/src/app/app.component.html +++ b/integration/cli-hello-world-lazy/src/app/app.component.html @@ -9,7 +9,9 @@ +
    -
    - +
      -
    • - -
      - - - - +
      + + + -
      - - - + (keyup)="doneEditing($event, todo)" + />
      -
    -
    @@ -62,7 +60,6 @@

    todos

    -
    diff --git a/modules/playground/src/zippy_component/app/zippy.html b/modules/playground/src/zippy_component/app/zippy.html index 4d5c36fb45c8..70763468b9ec 100644 --- a/modules/playground/src/zippy_component/app/zippy.html +++ b/modules/playground/src/zippy_component/app/zippy.html @@ -1,6 +1,6 @@
    - {{ visible ? '▾' : '▸' }} {{title}} + {{ visible ? '▾' : '▸' }} {{ title }}
    diff --git a/modules/ssr-benchmarks/dist/index.html b/modules/ssr-benchmarks/dist/index.html index 12459e3a1655..df28d2ca1ce5 100644 --- a/modules/ssr-benchmarks/dist/index.html +++ b/modules/ssr-benchmarks/dist/index.html @@ -1,8 +1,7 @@ - - - Check the console. - - - - \ No newline at end of file + + + Check the console. + + + diff --git a/modules/ssr-benchmarks/src/app/app.component.ts b/modules/ssr-benchmarks/src/app/app.component.ts index ab4d151a3716..7836a49e9fe6 100644 --- a/modules/ssr-benchmarks/src/app/app.component.ts +++ b/modules/ssr-benchmarks/src/app/app.component.ts @@ -15,11 +15,11 @@ import {testData} from '../../test-data'; template: ` - @for(entry of data; track $index) { - - - - + @for (entry of data; track $index) { + + + + }
    {{ entry.id }}{{ entry.name }}
    {{ entry.id }}{{ entry.name }}
    diff --git a/modules/ssr-benchmarks/src/index.html b/modules/ssr-benchmarks/src/index.html index 94c8f574914d..3691c373fde4 100644 --- a/modules/ssr-benchmarks/src/index.html +++ b/modules/ssr-benchmarks/src/index.html @@ -1,13 +1,13 @@ - - - SsrNew - - - - - - - + + + SsrNew + + + + + + + diff --git a/package.json b/package.json index dbb858452663..84e7711e5bc0 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { "name": "angular-srcs", - "version": "21.1.0-next.0", + "version": "21.1.0-next.4", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", "bugs": "https://github.com/angular/angular/issues", "license": "MIT", - "packageManager": "pnpm@10.23.0", + "packageManager": "pnpm@10.26.0", "engines": { "npm": "Please use pnpm instead of NPM to install dependencies", "yarn": "Please use pnpm instead of Yarn to install dependencies", - "pnpm": "10.23.0" + "pnpm": "10.26.0" }, "repository": { "type": "git", @@ -52,14 +52,14 @@ }, "// 1": "dependencies are used locally and by bazel", "dependencies": { - "@angular-devkit/build-angular": "21.1.0-next.0", - "@angular-devkit/core": "21.1.0-next.0", - "@angular-devkit/schematics": "21.1.0-next.0", + "@angular-devkit/build-angular": "21.1.0-next.2", + "@angular-devkit/core": "21.1.0-next.2", + "@angular-devkit/schematics": "21.1.0-next.2", "@angular/animations": "workspace:*", "@angular/benchpress": "workspace: *", - "@angular/build": "21.1.0-next.0", - "@angular/cdk": "21.1.0-next.0", - "@angular/cli": "21.1.0-next.0", + "@angular/build": "21.1.0-next.2", + "@angular/cdk": "21.1.0-next.3", + "@angular/cli": "21.1.0-next.2", "@angular/common": "workspace:*", "@angular/compiler": "workspace:*", "@angular/compiler-cli": "workspace:*", @@ -68,13 +68,13 @@ "@angular/forms": "workspace:*", "@angular/language-service": "workspace: *", "@angular/localize": "workspace: *", - "@angular/material": "21.1.0-next.0", + "@angular/material": "21.1.0-next.3", "@angular/platform-browser": "workspace:*", "@angular/platform-browser-dynamic": "workspace:*", "@angular/platform-server": "workspace:*", "@angular/router": "workspace:*", "@angular/service-worker": "workspace:*", - "@angular/ssr": "21.1.0-next.0", + "@angular/ssr": "21.1.0-next.2", "@angular/upgrade": "workspace: *", "@babel/cli": "7.28.3", "@babel/core": "7.28.5", @@ -84,7 +84,7 @@ "@rollup/plugin-babel": "^6.0.0", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-node-resolve": "^16.0.0", - "@schematics/angular": "21.1.0-next.0", + "@schematics/angular": "21.1.0-next.2", "@standard-schema/spec": "^1.0.0", "@types/angular": "^1.6.47", "@types/babel__core": "7.20.5", @@ -111,17 +111,17 @@ "angular-mocks-1.7": "npm:angular-mocks@1.7", "angular-mocks-1.8": "npm:angular-mocks@1.8", "chalk": "^5.4.1", - "chokidar": "^4.0.0", + "chokidar": "^5.0.0", "convert-source-map": "^1.5.1", "d3": "^7.0.0", "dagre-d3-es": "^7.0.11", "diff": "^8.0.0", "domino": "https://github.com/angular/domino.git#93e720f143d0296dd2726ffbcf4fc12283363a7b", - "esbuild": "0.27.0", + "esbuild": "0.27.1", "esbuild-plugin-umd-wrapper": "^3.0.0", "http-server": "^14.0.0", - "jasmine": "5.12.0", - "jasmine-core": "5.12.1", + "jasmine": "5.13.0", + "jasmine-core": "5.13.0", "jasmine-reporters": "^2.5.2", "karma": "~6.4.0", "karma-chrome-launcher": "^3.1.0", @@ -134,12 +134,12 @@ "open-in-idx": "^0.1.1", "protractor": "^7.0.0", "reflect-metadata": "^0.2.0", - "rollup": "4.53.3", + "rollup": "4.53.5", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-preserve-shebang": "^1.0.1", "rxjs": "^7.0.0", "selenium-webdriver": "3.5.0", - "selenium-webdriver4": "npm:selenium-webdriver@4.38.0", + "selenium-webdriver4": "npm:selenium-webdriver@4.39.0", "semver-dsl": "^1.0.1", "source-map": "0.7.6", "source-map-support": "0.5.21", @@ -160,15 +160,15 @@ }, "// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.", "devDependencies": { - "@actions/core": "^1.10.0", + "@actions/core": "^2.0.0", "@actions/github": "^6.0.0", - "@angular-devkit/architect-cli": "0.2101.0-next.0", - "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#4c28145df03aff8c74d0a53f4f5602140e4d1a23", + "@angular-devkit/architect-cli": "0.2101.0-next.2", + "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#24c98502339594196a800db33dd4294e359ea952", "@babel/plugin-proposal-async-generator-functions": "7.20.7", "@babel/plugin-transform-async-generator-functions": "^7.27.1", "@bazel/bazelisk": "^1.7.5", "@bazel/buildifier": "^8.0.0", - "@bazel/ibazel": "0.27.0", + "@bazel/ibazel": "0.28.0", "@inquirer/prompts": "^8.0.0", "@nginfra/angular-linking": "^1.0.10", "@octokit/graphql": "^9.0.0", @@ -179,11 +179,11 @@ "@types/tmp": "^0.2.6", "@yarnpkg/lockfile": "^1.1.0", "adm-zip": "^0.5.10", - "cldr": "7.9.0", + "cldr": "8.0.0", "cldrjs": "0.5.5", "conventional-changelog": "^7.0.0", - "cypress": "15.7.0", - "firebase-tools": "^14.0.0", + "cypress": "15.7.1", + "firebase-tools": "^15.0.0", "get-tsconfig": "^4.10.1", "gulp": "^5.0.0", "gulp-conventional-changelog": "^5.0.0", diff --git a/packages/benchpress/README.md b/packages/benchpress/README.md index 2f927ab24bac..99321816f5f9 100644 --- a/packages/benchpress/README.md +++ b/packages/benchpress/README.md @@ -165,29 +165,27 @@ It's also possible to measure any "user metric" within the browser by setting a numeric value on the `window` object. For example: ```js -bootstrap(App) - .then(() => { - window.timeToBootstrap = Date.now() - performance.timing.navigationStart; - }); +bootstrap(App).then(() => { + window.timeToBootstrap = Date.now() - performance.timing.navigationStart; +}); ``` A test driver for this user metric could be written as follows: ```js - -describe('home page load', function() { - it('should log load time for a 2G connection', done => { - runner.sample({ - execute: () => { - browser.get(`http://localhost:8080`); - }, - userMetrics: { - timeToBootstrap: 'The time in milliseconds to bootstrap' - }, - providers: [ - {provide: RegressionSlopeValidator.METRIC, useValue: 'timeToBootstrap'} - ] - }).then(done); +describe('home page load', function () { + it('should log load time for a 2G connection', (done) => { + runner + .sample({ + execute: () => { + browser.get(`http://localhost:8080`); + }, + userMetrics: { + timeToBootstrap: 'The time in milliseconds to bootstrap', + }, + providers: [{provide: RegressionSlopeValidator.METRIC, useValue: 'timeToBootstrap'}], + }) + .then(done); }); }); ``` diff --git a/packages/common/http/src/interceptor.ts b/packages/common/http/src/interceptor.ts index 07e72b65a855..98861f97b82e 100644 --- a/packages/common/http/src/interceptor.ts +++ b/packages/common/http/src/interceptor.ts @@ -192,14 +192,14 @@ export function chainedInterceptorFn( * @publicApi */ export const HTTP_INTERCEPTORS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'HTTP_INTERCEPTORS' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_INTERCEPTORS' : '', ); /** * A multi-provided token of `HttpInterceptorFn`s. */ export const HTTP_INTERCEPTOR_FNS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'HTTP_INTERCEPTOR_FNS' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_INTERCEPTOR_FNS' : '', {factory: () => []}, ); @@ -207,15 +207,16 @@ export const HTTP_INTERCEPTOR_FNS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'HTTP_ROOT_INTERCEPTOR_FNS' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_ROOT_INTERCEPTOR_FNS' : '', ); // TODO(atscott): We need a larger discussion about stability and what should contribute to stability. // Should the whole interceptor chain contribute to stability or just the backend request #55075? // Should HttpClient contribute to stability automatically at all? export const REQUESTS_CONTRIBUTE_TO_STABILITY = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'REQUESTS_CONTRIBUTE_TO_STABILITY' : '', - {providedIn: 'root', factory: () => true}, + typeof ngDevMode !== 'undefined' && ngDevMode ? 'REQUESTS_CONTRIBUTE_TO_STABILITY' : '', + // Providing a factory implies that the token is provided in root by default + {factory: () => true}, ); /** diff --git a/packages/common/http/src/provider.ts b/packages/common/http/src/provider.ts index 0fe763a4a912..dfee321420d6 100644 --- a/packages/common/http/src/provider.ts +++ b/packages/common/http/src/provider.ts @@ -156,7 +156,7 @@ export function withInterceptors( } const LEGACY_INTERCEPTOR_FN = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'LEGACY_INTERCEPTOR_FN' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'LEGACY_INTERCEPTOR_FN' : '', ); /** diff --git a/packages/common/http/src/transfer_cache.ts b/packages/common/http/src/transfer_cache.ts index 49f930b3caf6..b0bc58c3503e 100644 --- a/packages/common/http/src/transfer_cache.ts +++ b/packages/common/http/src/transfer_cache.ts @@ -60,7 +60,7 @@ export type HttpTransferCacheOptions = { * between those origins, so that `HttpTransferCache` feature can recognize those requests as the same * ones and reuse the data cached on the server during hydration on the client. * - * **Important note**: the `HTTP_TRANSFER_CACHE_ORIGIN_MAP` token should *only* be provided in + * IMPORTANT: The `HTTP_TRANSFER_CACHE_ORIGIN_MAP` token should *only* be provided in * the *server* code of your application (typically in the `app.server.config.ts` script). Angular throws an * error if it detects that the token is defined while running on the client. * @@ -81,7 +81,7 @@ export type HttpTransferCacheOptions = { * @publicApi */ export const HTTP_TRANSFER_CACHE_ORIGIN_MAP = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'HTTP_TRANSFER_CACHE_ORIGIN_MAP' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_TRANSFER_CACHE_ORIGIN_MAP' : '', ); /** @@ -115,7 +115,7 @@ interface CacheOptions extends HttpTransferCacheOptions { } const CACHE_OPTIONS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'HTTP_TRANSFER_STATE_CACHE_OPTIONS' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_TRANSFER_STATE_CACHE_OPTIONS' : '', ); /** diff --git a/packages/common/http/src/xsrf.ts b/packages/common/http/src/xsrf.ts index f0aa4c14227a..f6e485e4de89 100644 --- a/packages/common/http/src/xsrf.ts +++ b/packages/common/http/src/xsrf.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DOCUMENT, ɵparseCookieValue as parseCookieValue} from '../../index'; +import {DOCUMENT, ɵparseCookieValue as parseCookieValue, PlatformLocation} from '../../index'; import { EnvironmentInjector, inject, @@ -22,7 +22,7 @@ import {HttpRequest} from './request'; import {HttpEvent} from './response'; export const XSRF_ENABLED = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'XSRF_ENABLED' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'XSRF_ENABLED' : '', { factory: () => true, }, @@ -30,16 +30,16 @@ export const XSRF_ENABLED = new InjectionToken( export const XSRF_DEFAULT_COOKIE_NAME = 'XSRF-TOKEN'; export const XSRF_COOKIE_NAME = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'XSRF_COOKIE_NAME' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'XSRF_COOKIE_NAME' : '', { - providedIn: 'root', + // Providing a factory implies that the token is provided in root by default factory: () => XSRF_DEFAULT_COOKIE_NAME, }, ); export const XSRF_DEFAULT_HEADER_NAME = 'X-XSRF-TOKEN'; export const XSRF_HEADER_NAME = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'XSRF_HEADER_NAME' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'XSRF_HEADER_NAME' : '', { providedIn: 'root', factory: () => XSRF_DEFAULT_HEADER_NAME, @@ -91,25 +91,28 @@ export abstract class HttpXsrfTokenExtractor { abstract getToken(): string | null; } -/** - * Regex to match absolute URLs, including protocol-relative URLs. - */ -const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i; - export function xsrfInterceptorFn( req: HttpRequest, next: HttpHandlerFn, ): Observable> { - // Skip both non-mutating requests and absolute URLs. - // Non-mutating requests don't require a token, and absolute URLs require special handling - // anyway as the cookie set - // on our origin is not the same as the token expected by another origin. - if ( - !inject(XSRF_ENABLED) || - req.method === 'GET' || - req.method === 'HEAD' || - ABSOLUTE_URL_REGEX.test(req.url) - ) { + // Skip both non-mutating requests + // Non-mutating requests generally don't require a token. + if (!inject(XSRF_ENABLED) || req.method === 'GET' || req.method === 'HEAD') { + return next(req); + } + + try { + const locationHref = inject(PlatformLocation).href; + const {origin: locationOrigin} = new URL(locationHref); + // We can use `new URL` to normalize a relative URL like '//something.com' to + // 'https://something.com' in order to make consistent same-origin comparisons. + const {origin: requestOrigin} = new URL(req.url, locationOrigin); + + if (locationOrigin !== requestOrigin) { + return next(req); + } + } catch { + // Handle invalid URLs gracefully. return next(req); } diff --git a/packages/common/http/test/BUILD.bazel b/packages/common/http/test/BUILD.bazel index efebafb741a2..d99d6c3f807b 100644 --- a/packages/common/http/test/BUILD.bazel +++ b/packages/common/http/test/BUILD.bazel @@ -14,6 +14,7 @@ ts_project( "//packages/common", "//packages/common/http", "//packages/common/http/testing", + "//packages/common/testing", "//packages/core", "//packages/core/testing", "//packages/private/testing", diff --git a/packages/common/http/test/provider_spec.ts b/packages/common/http/test/provider_spec.ts index 516931cac864..1db5271adbcd 100644 --- a/packages/common/http/test/provider_spec.ts +++ b/packages/common/http/test/provider_spec.ts @@ -206,7 +206,7 @@ describe('provideHttpClient', () => { it('should allow injection from an interceptor context', () => { const ALPHA = new InjectionToken('alpha', { - providedIn: 'root', + // Providing a factory implies that the token is provided in root by default factory: () => 'alpha', }); const BETA = new InjectionToken('beta', {providedIn: 'root', factory: () => 'beta'}); diff --git a/packages/common/http/test/xsrf_spec.ts b/packages/common/http/test/xsrf_spec.ts index d0fd908ff707..ef0d6236fb97 100644 --- a/packages/common/http/test/xsrf_spec.ts +++ b/packages/common/http/test/xsrf_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DOCUMENT} from '../..'; +import {MockPlatformLocation} from '@angular/common/testing'; +import {DOCUMENT, PlatformLocation} from '../..'; import {HttpHeaders} from '../src/headers'; import {HttpRequest} from '../src/request'; import { @@ -47,24 +48,35 @@ describe('HttpXsrfInterceptor', () => { provide: XSRF_ENABLED, useValue: true, }, + { + provide: PlatformLocation, + useFactory: () => + new MockPlatformLocation({ + startUrl: 'http://sub.example.com/', + }), + }, HttpXsrfInterceptor, ], }); + interceptor = TestBed.inject(HttpXsrfInterceptor); backend = new HttpClientTestingBackend(); }); + it('applies XSRF protection to outgoing requests', () => { interceptor.intercept(new HttpRequest('POST', '/test', {}), backend).subscribe(); const req = backend.expectOne('/test'); expect(req.request.headers.get('X-XSRF-TOKEN')).toEqual('test'); req.flush({}); }); + it('does not apply XSRF protection when request is a GET', () => { interceptor.intercept(new HttpRequest('GET', '/test'), backend).subscribe(); const req = backend.expectOne('/test'); expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false); req.flush({}); }); + it('does not apply XSRF protection when request is a HEAD', () => { interceptor.intercept(new HttpRequest('HEAD', '/test'), backend).subscribe(); const req = backend.expectOne('/test'); @@ -72,7 +84,7 @@ describe('HttpXsrfInterceptor', () => { req.flush({}); }); - it('does not apply XSRF protection when request is absolute', () => { + it('does not apply XSRF protection when request is absolute and cross-origin', () => { interceptor .intercept(new HttpRequest('POST', 'https://example.com/test', {}), backend) .subscribe(); @@ -81,13 +93,31 @@ describe('HttpXsrfInterceptor', () => { req.flush({}); }); - it('does not apply XSRF protection when request is protocol relative', () => { + it('does not apply XSRF protection when request is protocol relative and cross-origin', () => { interceptor.intercept(new HttpRequest('POST', '//example.com/test', {}), backend).subscribe(); const req = backend.expectOne('//example.com/test'); expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse(); req.flush({}); }); + it('does apply XSRF protection when request is same-origin', () => { + interceptor + .intercept(new HttpRequest('POST', 'http://sub.example.com/test', {}), backend) + .subscribe(); + const req = backend.expectOne('http://sub.example.com/test'); + expect(req.request.headers.has('X-XSRF-TOKEN')).toBeTrue(); + req.flush({}); + }); + + it('does apply XSRF protection when request is protocol relative and same-origin', () => { + interceptor + .intercept(new HttpRequest('POST', '//sub.example.com/test', {}), backend) + .subscribe(); + const req = backend.expectOne('//sub.example.com/test'); + expect(req.request.headers.has('X-XSRF-TOKEN')).toBeTrue(); + req.flush({}); + }); + it('does not overwrite existing header', () => { interceptor .intercept( diff --git a/packages/common/locales/generate-locales-tool/README.md b/packages/common/locales/generate-locales-tool/README.md index 2f5f2eb963f9..1df7e7d58f45 100644 --- a/packages/common/locales/generate-locales-tool/README.md +++ b/packages/common/locales/generate-locales-tool/README.md @@ -23,7 +23,7 @@ const baseCurrencies = { 'CAD': ['CA$', '$', 2], 'NZD': ['NZ$', '$'], 'USD': ['$'], -} +}; ``` In the snippet above, you might wonder why values for `NZD` or `USD` are missing. This is intentional as for `USD` there is no narrow symbol (given `$` already being the symbol). The tool does not set an explicit value for byte savings. Same applies for the fraction digits. @@ -50,8 +50,8 @@ Additionally, if locale data is equal to locale data at a previous index, then t [ // ... labelsForDayPeriodsNarrow, - labelsForDayPeriodsAbbreviated -] + labelsForDayPeriodsAbbreviated, +]; ``` If `labelsForDayPeriodsAbbreviated` for example is equal to `labelsForDayPeriodsNarrow`, then the tool will not set a value for the abbreviated labels. Instead, it will set `undefined` as that minifies to: `[labelsForDayPeriodsNarrow, undefined]`. diff --git a/packages/common/src/directives/ng_optimized_image/image_loaders/image_loader.ts b/packages/common/src/directives/ng_optimized_image/image_loaders/image_loader.ts index ed569eb2facb..f3d5a0e4523f 100644 --- a/packages/common/src/directives/ng_optimized_image/image_loaders/image_loader.ts +++ b/packages/common/src/directives/ng_optimized_image/image_loaders/image_loader.ts @@ -71,7 +71,7 @@ export type ImageLoaderInfo = { * @publicApi */ export const IMAGE_LOADER = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'ImageLoader' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'ImageLoader' : '', { factory: () => noopImageLoader, }, diff --git a/packages/common/src/directives/ng_optimized_image/preconnect_link_checker.ts b/packages/common/src/directives/ng_optimized_image/preconnect_link_checker.ts index df135560d6fe..62c259b62940 100644 --- a/packages/common/src/directives/ng_optimized_image/preconnect_link_checker.ts +++ b/packages/common/src/directives/ng_optimized_image/preconnect_link_checker.ts @@ -43,7 +43,7 @@ const INTERNAL_PRECONNECT_CHECK_BLOCKLIST = new Set(['localhost', '127.0.0.1', ' * @publicApi */ export const PRECONNECT_CHECK_BLOCKLIST = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'PRECONNECT_CHECK_BLOCKLIST' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'PRECONNECT_CHECK_BLOCKLIST' : '', ); /** diff --git a/packages/common/src/location/location_strategy.ts b/packages/common/src/location/location_strategy.ts index d4556490b0c9..e5bbd332b772 100644 --- a/packages/common/src/location/location_strategy.ts +++ b/packages/common/src/location/location_strategy.ts @@ -76,7 +76,7 @@ export abstract class LocationStrategy { * @publicApi */ export const APP_BASE_HREF = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'appBaseHref' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'appBaseHref' : '', ); /** diff --git a/packages/common/src/location/platform_location.ts b/packages/common/src/location/platform_location.ts index b65e6034cfcd..649b403b66c6 100644 --- a/packages/common/src/location/platform_location.ts +++ b/packages/common/src/location/platform_location.ts @@ -73,7 +73,7 @@ export abstract class PlatformLocation { * @publicApi */ export const LOCATION_INITIALIZED = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'Location Initialized' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'Location Initialized' : '', ); /** diff --git a/packages/common/src/pipes/date_pipe.ts b/packages/common/src/pipes/date_pipe.ts index 1870d31f83cb..eafb04e1a2de 100644 --- a/packages/common/src/pipes/date_pipe.ts +++ b/packages/common/src/pipes/date_pipe.ts @@ -20,7 +20,7 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * @deprecated use DATE_PIPE_DEFAULT_OPTIONS token to configure DatePipe */ export const DATE_PIPE_DEFAULT_TIMEZONE = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'DATE_PIPE_DEFAULT_TIMEZONE' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'DATE_PIPE_DEFAULT_TIMEZONE' : '', ); /** @@ -55,7 +55,7 @@ export const DATE_PIPE_DEFAULT_TIMEZONE = new InjectionToken( * ``` */ export const DATE_PIPE_DEFAULT_OPTIONS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'DATE_PIPE_DEFAULT_OPTIONS' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'DATE_PIPE_DEFAULT_OPTIONS' : '', ); /** diff --git a/packages/common/testing/src/navigation/provide_fake_platform_navigation.ts b/packages/common/testing/src/navigation/provide_fake_platform_navigation.ts index 14d484e7884c..88686a514a8c 100644 --- a/packages/common/testing/src/navigation/provide_fake_platform_navigation.ts +++ b/packages/common/testing/src/navigation/provide_fake_platform_navigation.ts @@ -17,7 +17,7 @@ import { import {FakeNavigation} from './fake_navigation'; const FAKE_NAVIGATION = new InjectionToken('fakeNavigation', { - providedIn: 'root', + // Providing a factory implies that the token is provided in root by default factory: () => { const config = inject(MOCK_PLATFORM_LOCATION_CONFIG, {optional: true}); const baseFallback = 'http://_empty_/'; diff --git a/packages/common/upgrade/src/location_upgrade_module.ts b/packages/common/upgrade/src/location_upgrade_module.ts index 44ec81b687a0..e08029085ee6 100644 --- a/packages/common/upgrade/src/location_upgrade_module.ts +++ b/packages/common/upgrade/src/location_upgrade_module.ts @@ -56,11 +56,11 @@ export interface LocationUpgradeConfig { * @publicApi */ export const LOCATION_UPGRADE_CONFIGURATION = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'LOCATION_UPGRADE_CONFIGURATION' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'LOCATION_UPGRADE_CONFIGURATION' : '', ); const APP_BASE_HREF_RESOLVED = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'APP_BASE_HREF_RESOLVED' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'APP_BASE_HREF_RESOLVED' : '', ); /** diff --git a/packages/compiler-cli/package.json b/packages/compiler-cli/package.json index 5942c2140ff1..b57dace4289c 100644 --- a/packages/compiler-cli/package.json +++ b/packages/compiler-cli/package.json @@ -33,7 +33,7 @@ "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", "reflect-metadata": "^0.2.0", - "chokidar": "^4.0.0", + "chokidar": "^5.0.0", "convert-source-map": "^1.5.1", "semver": "^7.0.0", "tslib": "^2.3.0", diff --git a/packages/compiler-cli/src/ngtsc/docs/src/decorator_extractor.ts b/packages/compiler-cli/src/ngtsc/docs/src/decorator_extractor.ts index 189ccccd8137..acabfe924ec4 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/decorator_extractor.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/decorator_extractor.ts @@ -202,13 +202,18 @@ function getDecoratorJsDocNode( const decoratorInterface = getDecoratorInterface(declaration, typeChecker); // The public-facing JsDoc for each decorator is on one of its interface's call signatures. - const callSignature = decoratorInterface.members - .filter((node) => { + const callSignaturesWithJsDoc = decoratorInterface.members.filter( + (node): node is ts.CallSignatureDeclaration => { // The description block lives on one of the call signatures for this interface. - return ts.isCallSignatureDeclaration(node) && extractRawJsDoc(node); - }) - .at(-1); // Get the last one, as it is the most complete - + return ts.isCallSignatureDeclaration(node) && !!extractRawJsDoc(node); + }, + ); + + // There may be multiple call signatures, so we need to find the one with the + // longest JSDoc block. This is the one that is intended for public consumption. + const callSignature = callSignaturesWithJsDoc.sort( + (a, b) => (extractRawJsDoc(b)?.length ?? 0) - (extractRawJsDoc(a)?.length ?? 0), + )[0]; if (!callSignature || !ts.isCallSignatureDeclaration(callSignature)) { throw new Error(`No call signature with JsDoc on "${name}Decorator"`); } diff --git a/packages/compiler-cli/src/ngtsc/docs/src/entities.ts b/packages/compiler-cli/src/ngtsc/docs/src/entities.ts index a2b40bb5e4b1..9ee082606b9a 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/entities.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/entities.ts @@ -95,6 +95,7 @@ export interface DocEntryWithSourceInfo extends DocEntry { /** Base type for all documentation entities. */ export interface DocEntry { entryType: EntryType; + aliases?: string[]; name: string; description: string; rawComment: string; diff --git a/packages/compiler-cli/src/ngtsc/transform/src/implicit_signal_debug_name_transform.ts b/packages/compiler-cli/src/ngtsc/transform/src/implicit_signal_debug_name_transform.ts index f96abbb8c75c..c294ff889666 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/implicit_signal_debug_name_transform.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/implicit_signal_debug_name_transform.ts @@ -9,100 +9,94 @@ import ts from 'typescript'; function insertDebugNameIntoCallExpression( - callExpression: ts.CallExpression, + node: ts.CallExpression, debugName: string, ): ts.CallExpression { - const signalExpressionIsRequired = isRequiredSignalFunction(callExpression.expression); - let configPosition = signalExpressionIsRequired ? 0 : 1; - - // 1. If the call expression has no arguments, we pretend that the config object is at position 0. - // We do this so that we can insert a spread element at the start of the args list in a way where - // undefined can be the first argument but still get tree-shaken out in production builds. - // or - // 2. If the signal has an object-only definition (e.g. `linkedSignal` or `resource`), we set - // the argument position to 0, i.e. reusing the existing object. - const signalExpressionHasNoArguments = callExpression.arguments.length === 0; - const signalWithObjectOnlyDefinition = isSignalWithObjectOnlyDefinition(callExpression); - if (signalExpressionHasNoArguments || signalWithObjectOnlyDefinition) { - configPosition = 0; - } - - const nodeArgs = Array.from(callExpression.arguments); - let existingArgument = nodeArgs[configPosition]; - - if (existingArgument === undefined) { - existingArgument = ts.factory.createObjectLiteralExpression([]); - } - - // Do nothing if an identifier is used as the config object - // Ex: - // const defaultObject = { equals: () => false }; - // signal(123, defaultObject) - if (ts.isIdentifier(existingArgument)) { - return callExpression; - } + const isRequired = isRequiredSignalFunction(node.expression); + const hasNoArgs = node.arguments.length === 0; + const configPosition = hasNoArgs || isSignalWithObjectOnlyDefinition(node) || isRequired ? 0 : 1; + const existingArg = + configPosition >= node.arguments.length ? null : node.arguments[configPosition]; - if (!ts.isObjectLiteralExpression(existingArgument)) { - return callExpression; - } - - // Insert debugName into the existing config object - const properties = Array.from(existingArgument.properties); - const debugNameExists = properties.some( - (prop) => - ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'debugName', - ); - - if (debugNameExists) { - return callExpression; + // Do nothing if the existing parameter isn't statically analyzable or already has a `debugName`. + if ( + existingArg !== null && + (!ts.isObjectLiteralExpression(existingArg) || + existingArg.properties.some( + (prop) => + ts.isPropertyAssignment(prop) && + ts.isIdentifier(prop.name) && + prop.name.text === 'debugName', + )) + ) { + return node; } - const ngDevModeIdentifier = ts.factory.createIdentifier('ngDevMode'); const debugNameProperty = ts.factory.createPropertyAssignment( 'debugName', ts.factory.createStringLiteral(debugName), ); - const debugNameObject = ts.factory.createObjectLiteralExpression([debugNameProperty]); - const emptyObject = ts.factory.createObjectLiteralExpression(); - - // Create the spread expression: `...(ngDevMode ? { debugName: 'myDebugName' } : {})` - const spreadDebugNameExpression = ts.factory.createSpreadAssignment( - ts.factory.createParenthesizedExpression( - ts.factory.createConditionalExpression( - ngDevModeIdentifier, - undefined, // Question token - debugNameObject, - undefined, // Colon token - emptyObject, + + let newArgs: ts.Expression[]; + + if (existingArg !== null) { + // If there's an existing object literal already, we transform it as follows: + // `signal(0, {equal})` becomes `signal(0, { ...(ngDevMode ? {debugName: "n"} : {}), equal })`. + // During minification the spread will be removed since it's pointing to an empty object. + const transformedArg = ts.factory.createObjectLiteralExpression([ + ts.factory.createSpreadAssignment( + createNgDevModeConditional( + ts.factory.createObjectLiteralExpression([debugNameProperty]), + ts.factory.createObjectLiteralExpression(), + ), ), - ), - ); + ...existingArg.properties, + ]); - const transformedConfigProperties = ts.factory.createObjectLiteralExpression([ - spreadDebugNameExpression, - ...properties, - ]); - - let transformedSignalArgs = []; - - // The following expression handles 3 cases: - // 1. Non-`required` signals without an argument that need to be prepended with `undefined` (e.g. `model()`). - // 2. Signals with object-only definition like `resource` or `linkedSignal` with computation; - // Or `required` signals. - // 3. All remaining cases where we have a signal with an argument (e.g `computed(Fn)` or `signal('foo')`). - if (signalExpressionHasNoArguments && !signalExpressionIsRequired) { - transformedSignalArgs = [ts.factory.createIdentifier('undefined'), transformedConfigProperties]; - } else if (signalWithObjectOnlyDefinition || signalExpressionIsRequired) { - transformedSignalArgs = [transformedConfigProperties]; + newArgs = node.arguments.map((arg) => (arg === existingArg ? transformedArg : arg)); } else { - transformedSignalArgs = [nodeArgs[0], transformedConfigProperties]; + // If there's no existing argument, we transform it as follows: + // `input(0)` becomes `input(0, ...(ngDevMode ? [{debugName: "n"}] : []))` + // Spreading into an empty literal allows for the array to be dropped during minification. + const spreadArgs: ts.Expression[] = []; + + // If we're adding an argument, but the function requires a first argument (e.g. `input()`), + // we have to add `undefined` before the debug literal. + if (hasNoArgs && !isRequired) { + spreadArgs.push(ts.factory.createIdentifier('undefined')); + } + + spreadArgs.push(ts.factory.createObjectLiteralExpression([debugNameProperty])); + + newArgs = [ + ...node.arguments, + ts.factory.createSpreadElement( + createNgDevModeConditional( + ts.factory.createArrayLiteralExpression(spreadArgs), + ts.factory.createArrayLiteralExpression(), + ), + ), + ]; } - return ts.factory.updateCallExpression( - callExpression, - callExpression.expression, - callExpression.typeArguments, - ts.factory.createNodeArray(transformedSignalArgs), + return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArgs); +} + +/** + * Creates an expression in the form of `(ngDevMode ? : )`. + */ +function createNgDevModeConditional( + devModeExpression: ts.Expression, + prodModeExpression: ts.Expression, +): ts.ParenthesizedExpression { + return ts.factory.createParenthesizedExpression( + ts.factory.createConditionalExpression( + ts.factory.createIdentifier('ngDevMode'), + undefined, + devModeExpression, + undefined, + prodModeExpression, + ), ); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/README.md b/packages/compiler-cli/src/ngtsc/typecheck/README.md index f762d79d3300..a8ea4f974f89 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/README.md +++ b/packages/compiler-cli/src/ngtsc/typecheck/README.md @@ -52,8 +52,7 @@ If `SomeCmp` does not have a `foo` property, then TypeScript will produce a type Not only can a template consume properties declared from its component, but various structures within a template can also be considered "declarations" which have types of their own. For example, the template: ```html - -{{name.value}} + {{name.value}} ``` declares a single `` element with a local ref `#name`, meaning that within this template `name` refers to the `` element. The `{{name.value}}` interpolation is reading the `value` property of this element. @@ -118,7 +117,6 @@ For directives, neither of these options makes sense. Users do not write direct Instead, conceptually, the generic type of a directive depends on the types bound to its _inputs_. This is immediately evident for a directive such as `NgFor`: ```typescript - @Directive({selector: '[ngFor]'}) export class NgFor { @Input() ngForOf!: Iterable; @@ -159,9 +157,7 @@ A single type constructor for a directive can be used in multiple places, whenev `NgFor` is a structural directive, meaning that it applies to a nested ``. That is, the template: ```html -
    - {{user.name}} -
    +
    {{user.name}}
    ``` is syntactic sugar for: @@ -249,9 +245,7 @@ Because the `NgFor` directive _declared_ to the template type checking engine wh `NgIf` requires a similar, albeit not identical, operation to perform type narrowing with its nested template. Instead of narrowing the template context, `NgIf` wants to narrow the actual type of the expression within its binding. Consider the template: ```html -
    - {{user.name}} -
    +
    {{user.name}}
    ``` Obviously, if `user` is potentially `null`, then this `NgIf` is intended to only show the `
    ` when `user` actually has a value. However, from a type-checking perspective, the expression `user.name` is not legal if `user` is potentially `null`. So if this template was rendered into a TCB as: @@ -308,8 +302,7 @@ The guard expression causes TypeScript to narrow the type of `this.user` within Angular templates allow forward references. For example, the template: ```html -The value is: {{in.value}} - +The value is: {{in.value}} ``` contains an expression which makes use of the `#in` local reference before the targeted `` element is declared. Since such forward references are not legal in TypeScript code, the TCB may need to declare and check template structures in a different order than the template itself. @@ -332,10 +325,7 @@ The main algorithm for TCB generation then makes use of this abstraction: This potential out-of-order execution of `TcbOp`s allows for the TCB ordering to support forward references within templates. The above forward reference example thus results in a `TcbOp` queue of two operations: ```typescript -[ - TcbTextInterpolationOp(`in.value`), - TcbElementOp(''), -] +[TcbTextInterpolationOp(`in.value`), TcbElementOp('')]; ``` Execution of the first `TcbTextInterpolationOp` will attempt to generate code representing the expression. Doing this requires knowing the type of the `in` reference, which maps to the element node for the ``. Therefore, as part of executing the `TcbTextInterpolationOp`, the execution of the `TcbElementOp` will be requested. This operation produces TCB code for the element: @@ -449,7 +439,7 @@ The generated TCB code for this expression would look like: What actually gets generated for this expression looks more like: ```typescript -'' + (this.foo /* 3,5 */).bar /* 3,9 */; +'' + this.foo /* 3,5 */.bar /* 3,9 */; ``` The trailing comment for each node in the TCB indicates the template offsets for the corresponding template nodes. If for example TypeScript returns a diagnostic for the `this.foo` part of the expression (such as if `foo` is not a valid property on the component context), the attached comment can be used to map this diagnostic back to the original template's `foo` node. @@ -524,7 +514,6 @@ export class MyDir { In such cases, the type checking system falls back to an alternative mechanism for declaring type constructors: adding them as static methods on the directive class itself. As part of the type checking phase, the above directive would be transformed to: ```typescript - interface PrivateInterface { field: string; } @@ -533,7 +522,9 @@ interface PrivateInterface { export class MyDir { @Input() value: T; - static ngTypeCtor(inputs: {value?: T}): MyDir { return null!; } + static ngTypeCtor(inputs: {value?: T}): MyDir { + return null!; + } } ``` diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/scope.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/scope.ts index 4b0474d5a1fc..fc0d18c1d3e3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/scope.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/scope.ts @@ -56,7 +56,8 @@ import { getCustomFieldDirectiveType, isFieldDirective, isNativeField, - TcbNativeFieldDirectiveTypeOp, + TcbNativeFieldOp, + TcbNativeRadioButtonFieldOp, } from './signal_forms'; import {Reference} from '../../../imports'; import {ClassDeclaration} from '../../../reflection'; @@ -774,7 +775,15 @@ export class Scope { dirMap.set(dir, dirIndex); if (isNativeField(dir, node, allDirectiveMatches)) { - this.opQueue.push(new TcbNativeFieldDirectiveTypeOp(this.tcb, this, node as TmplAstElement)); + const inputType = + (node.name === 'input' && node.attributes.find((attr) => attr.name === 'type')?.value) || + null; + + this.opQueue.push( + inputType === 'radio' + ? new TcbNativeRadioButtonFieldOp(this.tcb, this, node) + : new TcbNativeFieldOp(this.tcb, this, node, inputType), + ); } this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir, customFieldType)); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts index e497b7d6cbdb..884a92d579ff 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts @@ -74,7 +74,7 @@ const formControlOptionalFields = new Set([ /** * A `TcbOp` which constructs an instance of the signal forms `Field` directive on a native element. */ -export class TcbNativeFieldDirectiveTypeOp extends TcbOp { +export class TcbNativeFieldOp extends TcbOp { /** Bindings that aren't supported on signal form fields. */ protected readonly unsupportedBindingFields = new Set([ ...formControlInputFields, @@ -84,27 +84,17 @@ export class TcbNativeFieldDirectiveTypeOp extends TcbOp { 'minlength', ]); - private readonly inputType: string | null; - override get optional() { return false; } constructor( - private tcb: Context, - private scope: Scope, - private node: TmplAstElement, + protected tcb: Context, + protected scope: Scope, + protected node: TmplAstElement, + private inputType: string | null, ) { super(); - - this.inputType = - (node.name === 'input' && node.attributes.find((attr) => attr.name === 'type')?.value) || - null; - - // Radio controls are allowed to set the `value`. - if (this.inputType === 'radio') { - this.unsupportedBindingFields.delete('value'); - } } override execute(): null { @@ -120,11 +110,7 @@ export class TcbNativeFieldDirectiveTypeOp extends TcbOp { checkUnsupportedFieldBindings(this.node, this.unsupportedBindingFields, this.tcb); - const expectedType = - this.node instanceof TmplAstElement - ? this.getExpectedTypeFromDomNode(this.node) - : this.getUnsupportedType(); - + const expectedType = this.getExpectedTypeFromDomNode(this.node); const value = extractFieldValue(fieldBinding.value, this.tcb, this.scope); // Create a variable with the expected type and check that the field value is assignable, e.g. @@ -171,6 +157,25 @@ export class TcbNativeFieldDirectiveTypeOp extends TcbOp { ]); } + const hasDynamicType = + this.inputType === null && + this.node.inputs.some( + (input) => + (input.type === BindingType.Property || input.type === BindingType.Attribute) && + input.name === 'type', + ); + + // If the type is dynamic, check it as if it can be any of the types above. + if (hasDynamicType) { + return ts.factory.createUnionTypeNode([ + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), + ts.factory.createTypeReferenceNode('Date'), + ts.factory.createLiteralTypeNode(ts.factory.createNull()), + ]); + } + // Fall back to string if we couldn't map the type. return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); } @@ -180,6 +185,38 @@ export class TcbNativeFieldDirectiveTypeOp extends TcbOp { } } +/** + * A variation of the `TcbNativeFieldOp` with specific logic for radio buttons. + */ +export class TcbNativeRadioButtonFieldOp extends TcbNativeFieldOp { + constructor(tcb: Context, scope: Scope, node: TmplAstElement) { + super(tcb, scope, node, 'radio'); + this.unsupportedBindingFields.delete('value'); + } + + override execute(): null { + super.execute(); + + const valueBinding = this.node.inputs.find((attr) => { + return attr.type === BindingType.Property && attr.name === 'value'; + }); + + if (valueBinding !== undefined) { + // Include an additional expression to check that the `value` is a string. + const id = this.tcb.allocateId(); + const value = tcbExpression(valueBinding.value, this.tcb, this.scope); + const assignment = ts.factory.createBinaryExpression(id, ts.SyntaxKind.EqualsToken, value); + addParseSpanInfo(assignment, valueBinding.sourceSpan); + this.scope.addStatement( + tsDeclareVariable(id, ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)), + ); + this.scope.addStatement(ts.factory.createExpressionStatement(assignment)); + } + + return null; + } +} + /** Expands the set of bound inputs with the ones from custom field directives. */ export function expandBoundAttributesForField( directive: TypeCheckableDirectiveMeta, @@ -319,7 +356,7 @@ export function isNativeField( dir: TypeCheckableDirectiveMeta, node: TmplAstNode, allDirectiveMatches: TypeCheckableDirectiveMeta[], -): boolean { +): node is TmplAstElement & {name: 'input' | 'select' | 'textarea'} { // Only applies to the `Field` directive. if (!isFieldDirective(dir)) { return false; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index eb1f7792361d..c131e6b2fe09 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -2603,6 +2603,22 @@ describe('type check blocks', () => { expect(block).toContain('_t2.field = (((this).f));'); }); + it('should generate all possible type for input with a dynamic `type`', () => { + const block = tcb('', [FieldMock]); + expect(block).toContain('var _t1 = null! as string | number | boolean | Date | null;'); + expect(block).toContain('_t1 = ((this).f)().value();'); + expect(block).toContain('var _t2 = null! as i0.Field;'); + expect(block).toContain('_t2.field = (((this).f));'); + }); + + it('should generate all possible type for input with a dynamic attribute `type` binding', () => { + const block = tcb('', [FieldMock]); + expect(block).toContain('var _t1 = null! as string | number | boolean | Date | null;'); + expect(block).toContain('_t1 = ((this).f)().value();'); + expect(block).toContain('var _t2 = null! as i0.Field;'); + expect(block).toContain('_t2.field = (((this).f));'); + }); + [ {inputType: 'text', expectedType: 'string'}, {inputType: 'radio', expectedType: 'string'}, @@ -2625,6 +2641,16 @@ describe('type check blocks', () => { }); }); + it('should generate expressions to check the field and value bindings of a radio input', () => { + const block = tcb('', [FieldMock]); + expect(block).toContain('var _t1 = null! as string;'); + expect(block).toContain('_t1 = ((this).f)().value();'); + expect(block).toContain('var _t2 = null! as string;'); + expect(block).toContain('_t2 = ((this).expr);'); + expect(block).toContain('var _t3 = null! as i0.Field;'); + expect(block).toContain('_t3.field = (((this).f));'); + }); + it('should generate a string field for a textarea', () => { const block = tcb('', [FieldMock]); expect(block).toContain('var _t1 = null! as string;'); diff --git a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl index 98e905cbd3c4..74e81baea3af 100644 --- a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl +++ b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl @@ -13,7 +13,7 @@ def partial_compliance_golden(filePath): "//packages/core:npm_package", "//packages:package_json", filePath, - ] + native.glob(["%s/*.ts" % path, "%s/**/*.html" % path, "%s/**/*.css" % path]) + ] + native.glob(["%s/*.ts" % path, "%s/**/*.html" % path, "%s/**/*.css" % path], allow_empty = True) js_binary( name = generate_partial_name, diff --git a/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js index 94a117e7c626..31fb69d34e58 100644 --- a/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js @@ -5,8 +5,8 @@ import { Directive, model } from '@angular/core'; import * as i0 from "@angular/core"; export class TestDir { constructor() { - this.counter = model(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); - this.name = model.required(Object.assign({}, (ngDevMode ? { debugName: "name" } : {}))); + this.counter = model(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); + this.name = model.required(...(ngDevMode ? [{ debugName: "name" }] : [])); } } TestDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); @@ -34,8 +34,8 @@ import { Component, model } from '@angular/core'; import * as i0 from "@angular/core"; export class TestComp { constructor() { - this.counter = model(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); - this.name = model.required(Object.assign({}, (ngDevMode ? { debugName: "name" } : {}))); + this.counter = model(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); + this.name = model.required(...(ngDevMode ? [{ debugName: "name" }] : [])); } } TestComp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, deps: [], target: i0.ɵɵFactoryTarget.Component }); @@ -65,7 +65,7 @@ import { Directive, EventEmitter, Input, model, Output } from '@angular/core'; import * as i0 from "@angular/core"; export class TestDir { constructor() { - this.counter = model(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); + this.counter = model(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); this.modelWithAlias = model(false, Object.assign(Object.assign({}, (ngDevMode ? { debugName: "modelWithAlias" } : {})), { alias: 'alias' })); this.decoratorInput = true; this.decoratorInputWithAlias = true; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js index 6c431809d774..b21c8ab175f6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js @@ -3,11 +3,10 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5)(dirIndex, SomeDirective, 4); } if (rf & 2) { - let $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDirList = $tmp$); } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js index 3a78c30a1bd9..1702d2f6791f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js @@ -5,8 +5,7 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5)(dirIndex, $e1_attrs$, 4); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_forward_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_forward_ref.js index a3b4e158d7a8..adfe3a96393d 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_forward_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_forward_ref.js @@ -3,8 +3,7 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, __QueryFlags.emitDistinctChangesOnly__); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__)(dirIndex, SomeDirective, __QueryFlags.emitDistinctChangesOnly__); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js index 6de5bc94e3a5..81a4bd6ef7d3 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js @@ -5,13 +5,10 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5, TemplateRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5, TemplateRef)(dirIndex, SomeDirective, 5, ElementRef)(dirIndex, $e1_attrs$, 4, ElementRef)(dirIndex, SomeDirective, 4, TemplateRef); } if (rf & 2) { - let $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRef = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/query_with_emit_distinct_changes_only.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/query_with_emit_distinct_changes_only.js index 60185a0c70d8..332db18cfe85 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/query_with_emit_distinct_changes_only.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/query_with_emit_distinct_changes_only.js @@ -4,8 +4,7 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.emitDistinctChangesOnly__); - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.none__); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.emitDistinctChangesOnly__)(dirIndex, $e0_attrs$, __QueryFlags.none__); } if (rf & 2) { let $tmp$; @@ -16,8 +15,7 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... viewQuery: function ContentQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.emitDistinctChangesOnly__|__QueryFlags.descendants__); - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__); + $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.emitDistinctChangesOnly__|__QueryFlags.descendants__)(SomeDirective, __QueryFlags.descendants__); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js index 7000633bcaed..eec828abb85f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js @@ -3,9 +3,7 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery( - dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); - $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 5); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__)(dirIndex, $ref0$, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js index 8515ab2a27d3..ddfafd684858 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js @@ -5,8 +5,7 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); - $r3$.ɵɵviewQuery($refs$, 5); + $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__)($refs$, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js index 7cc3bedecb8e..4f495b5e0e4b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js @@ -3,8 +3,7 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, 5); - $r3$.ɵɵviewQuery(SomeDirective, 5); + $r3$.ɵɵviewQuery(SomeDirective, 5)(SomeDirective, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js index d8770b0d9b8a..c88a4ba6b2fc 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js @@ -5,8 +5,7 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 5); - $r3$.ɵɵviewQuery($e1_attrs$, 5); + $r3$.ɵɵviewQuery($e0_attrs$, 5)($e1_attrs$, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_forward_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_forward_ref.js index 6f04af70e9cb..f98c537d1830 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_forward_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_forward_ref.js @@ -3,8 +3,7 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); + $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__)(SomeDirective, __QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js index 247b2fd807b9..a9667f22b326 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js @@ -5,10 +5,7 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 5, TemplateRef); - $r3$.ɵɵviewQuery(SomeDirective, 5, ElementRef); - $r3$.ɵɵviewQuery($e1_attrs$, 5, ElementRef); - $r3$.ɵɵviewQuery(SomeDirective, 5, TemplateRef); + $r3$.ɵɵviewQuery($e0_attrs$, 5, TemplateRef)(SomeDirective, 5, ElementRef)($e1_attrs$, 5, ElementRef)(SomeDirective, 5, TemplateRef); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js index 42d1bdb67035..e73053fe6f35 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js @@ -3,6 +3,6 @@ template: function MyComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "iframe", 0); } if (rf & 2) { - i0.ɵɵattribute("fetchpriority", "low", i0.ɵɵvalidateIframeAttribute)("allowfullscreen", ctx.fullscreen, i0.ɵɵvalidateIframeAttribute); + i0.ɵɵattribute("fetchpriority", "low", i0.ɵɵvalidateAttribute)("allowfullscreen", ctx.fullscreen, i0.ɵɵvalidateAttribute); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js index 51562fe9d9f9..d3bec64a5c40 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js @@ -188,7 +188,7 @@ import { Component, signal } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { constructor() { - this.enterClass = signal('slide', Object.assign({}, (ngDevMode ? { debugName: "enterClass" } : {}))); + this.enterClass = signal('slide', ...(ngDevMode ? [{ debugName: "enterClass" }] : [])); } } MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); @@ -299,7 +299,7 @@ import { Component, signal } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { constructor() { - this.leaveClass = signal('fade', Object.assign({}, (ngDevMode ? { debugName: "leaveClass" } : {}))); + this.leaveClass = signal('fade', ...(ngDevMode ? [{ debugName: "leaveClass" }] : [])); } } MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/GOLDEN_PARTIAL.js index 5c422e559954..22d846539c69 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/GOLDEN_PARTIAL.js @@ -5,7 +5,7 @@ import { Component, Directive, input } from '@angular/core'; import * as i0 from "@angular/core"; export class Field { constructor() { - this.field = input(undefined, Object.assign({}, (ngDevMode ? { debugName: "field" } : {}))); + this.field = input(...(ngDevMode ? [undefined, { debugName: "field" }] : [])); } } Field.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Field, deps: [], target: i0.ɵɵFactoryTarget.Directive }); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/control_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/control_bindings.js index 9934dcbfdc28..56c30da62634 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/control_bindings.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/control_bindings/control_bindings.js @@ -10,9 +10,8 @@ MyComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({ i0.ɵɵelementStart(1, "div"); i0.ɵɵtext(2, "Not a form control either."); i0.ɵɵelementEnd(); - i0.ɵɵelementStart(3, "input", 1); + i0.ɵɵelement(3, "input", 1); i0.ɵɵcontrolCreate(); - i0.ɵɵelementEnd(); } if (rf & 2) { i0.ɵɵadvance(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js index 47fa28574425..a9a6286eed0d 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js @@ -905,10 +905,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE export class HostBindingImageDir { constructor() { this.evil = 'evil'; + this.nonEvil = 'nonEvil'; } } HostBindingImageDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingImageDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); -HostBindingImageDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingImageDir, isStandalone: true, selector: "img[hostBindingImgDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "evil" } }, ngImport: i0 }); +HostBindingImageDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingImageDir, isStandalone: true, selector: "img[hostBindingImgDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "nonEvil" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingImageDir, decorators: [{ type: Directive, args: [{ @@ -916,17 +917,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE host: { '[innerHtml]': 'evil', '[attr.style]': 'evil', - '[src]': 'evil', + '[src]': 'nonEvil', }, }] }] }); export class HostBindingIframeDir { constructor() { this.evil = 'evil'; + this.nonEvil = 'nonEvil'; } } HostBindingIframeDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingIframeDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); -HostBindingIframeDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingIframeDir, isStandalone: true, selector: "iframe[hostBindingIframeDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "evil", "sandbox": "evil" } }, ngImport: i0 }); +HostBindingIframeDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingIframeDir, isStandalone: true, selector: "iframe[hostBindingIframeDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "evil", "sandbox": "evil", "attr.attributeName": "nonEvil" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingIframeDir, decorators: [{ type: Directive, args: [{ @@ -936,6 +938,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE '[attr.style]': 'evil', '[src]': 'evil', '[sandbox]': 'evil', + '[attr.attributeName]': 'nonEvil', + }, + }] + }] }); +export class HostBindingSvgAnimateDir { + constructor() { + this.evil = 'evil'; + } +} +HostBindingSvgAnimateDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingSvgAnimateDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); +HostBindingSvgAnimateDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingSvgAnimateDir, isStandalone: true, selector: "animateMotion[hostBindingSvgAnimateDir]", host: { properties: { "attr.attributeName": "evil" } }, ngImport: i0 }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingSvgAnimateDir, decorators: [{ + type: Directive, + args: [{ + selector: 'animateMotion[hostBindingSvgAnimateDir]', + host: { + '[attr.attributeName]': 'evil', }, }] }] }); @@ -951,14 +970,21 @@ export declare class HostBindingLinkDir { } export declare class HostBindingImageDir { evil: string; + nonEvil: string; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵdir: i0.ɵɵDirectiveDeclaration; } export declare class HostBindingIframeDir { evil: string; + nonEvil: string; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵdir: i0.ɵɵDirectiveDeclaration; } +export declare class HostBindingSvgAnimateDir { + evil: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} /**************************************************************************************************** * PARTIAL FILE: security_sensitive_constant_attributes.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.js index ca216aacac71..9ea142fafedc 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.js @@ -7,14 +7,20 @@ hostBindings: function HostBindingLinkDir_HostBindings(rf, ctx) { … hostBindings: function HostBindingImageDir_HostBindings(rf, ctx) { if (rf & 2) { - $r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, $r3$.ɵɵsanitizeUrl); - $r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle); + i0.ɵɵdomProperty("innerHTML", ctx.evil, i0.ɵɵsanitizeHtml)("src", ctx.nonEvil, i0.ɵɵsanitizeUrl); + i0.ɵɵattribute("style", ctx.evil, i0.ɵɵsanitizeStyle); } } … hostBindings: function HostBindingIframeDir_HostBindings(rf, ctx) { if (rf & 2) { - $r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, $r3$.ɵɵsanitizeResourceUrl)("sandbox", ctx.evil, $r3$.ɵɵvalidateIframeAttribute); - $r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle); + $r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, i0.ɵɵsanitizeResourceUrl)("sandbox", ctx.evil, $r3$.ɵɵvalidateAttribute); + $r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle)("attributeName", ctx.nonEvil); } } +… +hostBindings: function HostBindingSvgAnimateDir_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵattribute("attributeName", ctx.evil, i0.ɵɵvalidateAttribute); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.ts index 7afa74caf8a1..92a17ab2e3f5 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization.ts @@ -17,11 +17,12 @@ export class HostBindingLinkDir { host: { '[innerHtml]': 'evil', '[attr.style]': 'evil', - '[src]': 'evil', + '[src]': 'nonEvil', }, }) export class HostBindingImageDir { evil = 'evil'; + nonEvil = 'nonEvil'; } @Directive({ @@ -31,8 +32,20 @@ export class HostBindingImageDir { '[attr.style]': 'evil', '[src]': 'evil', '[sandbox]': 'evil', + '[attr.attributeName]': 'nonEvil', }, }) export class HostBindingIframeDir { evil = 'evil'; + nonEvil = 'nonEvil'; +} + +@Directive({ + selector: 'animateMotion[hostBindingSvgAnimateDir]', + host: { + '[attr.attributeName]': 'evil', + }, +}) +export class HostBindingSvgAnimateDir { + evil = 'evil'; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js index 0a020a2d22b0..db3704691e04 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js @@ -827,6 +827,7 @@ import * as i0 from "@angular/core"; export class MyComponent { constructor() { this.evil = 'evil'; + this.nonEvil = 'nonEvil'; } } MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); @@ -834,7 +835,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
    - +
    @@ -847,7 +848,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
    - +
    @@ -861,6 +862,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE import * as i0 from "@angular/core"; export declare class MyComponent { evil: string; + nonEvil: string; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵcmp: i0.ɵɵComponentDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.js index 340001152844..dc3f88510887 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.js @@ -9,9 +9,9 @@ template: function MyComponent_Template(rf, ctx) { $r3$.ɵɵadvance(); $r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle); $r3$.ɵɵadvance(); - $r3$.ɵɵdomProperty("src", ctx.evil, $r3$.ɵɵsanitizeUrl); + $r3$.ɵɵdomProperty("src", ctx.nonEvil, $r3$.ɵɵsanitizeUrl); $r3$.ɵɵadvance(); - $r3$.ɵɵdomProperty("sandbox", ctx.evil, $r3$.ɵɵvalidateIframeAttribute); + $r3$.ɵɵdomProperty("sandbox", ctx.evil, $r3$.ɵɵvalidateAttribute); $r3$.ɵɵadvance(); $r3$.ɵɵdomProperty("href", $r3$.ɵɵinterpolate2("", ctx.evil, "", ctx.evil), $r3$.ɵɵsanitizeUrl); $r3$.ɵɵadvance(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.ts index ba821b80bc4e..ece0f51a4c02 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/sanitization.ts @@ -6,7 +6,7 @@ import {Component} from '@angular/core';
    - +
    @@ -14,4 +14,5 @@ import {Component} from '@angular/core'; }) export class MyComponent { evil = 'evil'; + nonEvil = 'nonEvil'; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js index 203a15c48c9b..4b1957b46610 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js @@ -967,7 +967,7 @@ import { Component, Directive, model, signal } from '@angular/core'; import * as i0 from "@angular/core"; export class NgModelDirective { constructor() { - this.ngModel = model.required(Object.assign({}, (ngDevMode ? { debugName: "ngModel" } : {}))); + this.ngModel = model.required(...(ngDevMode ? [{ debugName: "ngModel" }] : [])); } } NgModelDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NgModelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); @@ -1023,7 +1023,7 @@ import { Component, Directive, model } from '@angular/core'; import * as i0 from "@angular/core"; export class NgModelDirective { constructor() { - this.ngModel = model('', Object.assign({}, (ngDevMode ? { debugName: "ngModel" } : {}))); + this.ngModel = model('', ...(ngDevMode ? [{ debugName: "ngModel" }] : [])); } } NgModelDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NgModelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js index 38ac7ff1f95d..0e8ad488f3eb 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js @@ -5,8 +5,8 @@ import { Directive, input } from '@angular/core'; import * as i0 from "@angular/core"; export class TestDir { constructor() { - this.counter = input(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); - this.name = input.required(Object.assign({}, (ngDevMode ? { debugName: "name" } : {}))); + this.counter = input(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); + this.name = input.required(...(ngDevMode ? [{ debugName: "name" }] : [])); } } TestDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); @@ -34,8 +34,8 @@ import { Component, input } from '@angular/core'; import * as i0 from "@angular/core"; export class TestComp { constructor() { - this.counter = input(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); - this.name = input.required(Object.assign({}, (ngDevMode ? { debugName: "name" } : {}))); + this.counter = input(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); + this.name = input.required(...(ngDevMode ? [{ debugName: "name" }] : [])); } } TestComp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, deps: [], target: i0.ɵɵFactoryTarget.Component }); @@ -68,7 +68,7 @@ function convertToBoolean(value) { } export class TestDir { constructor() { - this.counter = input(0, Object.assign({}, (ngDevMode ? { debugName: "counter" } : {}))); + this.counter = input(0, ...(ngDevMode ? [{ debugName: "counter" }] : [])); this.signalWithTransform = input(false, Object.assign(Object.assign({}, (ngDevMode ? { debugName: "signalWithTransform" } : {})), { transform: convertToBoolean })); this.signalWithTransformAndAlias = input(false, Object.assign(Object.assign({}, (ngDevMode ? { debugName: "signalWithTransformAndAlias" } : {})), { alias: 'publicNameSignal', transform: convertToBoolean })); this.decoratorInput = true; diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js index ce703bd53f85..78ffce89ad62 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js @@ -8,12 +8,12 @@ export class SomeToken { const nonAnalyzableRefersToString = 'a, b, c'; export class TestDir { constructor() { - this.query1 = viewChild('locatorA', Object.assign({}, (ngDevMode ? { debugName: "query1" } : {}))); - this.query2 = viewChildren('locatorB', Object.assign({}, (ngDevMode ? { debugName: "query2" } : {}))); - this.query3 = contentChild('locatorC', Object.assign({}, (ngDevMode ? { debugName: "query3" } : {}))); - this.query4 = contentChildren('locatorD', Object.assign({}, (ngDevMode ? { debugName: "query4" } : {}))); - this.query5 = viewChild(forwardRef(() => SomeToken), Object.assign({}, (ngDevMode ? { debugName: "query5" } : {}))); - this.query6 = viewChildren(SomeToken, Object.assign({}, (ngDevMode ? { debugName: "query6" } : {}))); + this.query1 = viewChild('locatorA', ...(ngDevMode ? [{ debugName: "query1" }] : [])); + this.query2 = viewChildren('locatorB', ...(ngDevMode ? [{ debugName: "query2" }] : [])); + this.query3 = contentChild('locatorC', ...(ngDevMode ? [{ debugName: "query3" }] : [])); + this.query4 = contentChildren('locatorD', ...(ngDevMode ? [{ debugName: "query4" }] : [])); + this.query5 = viewChild(forwardRef(() => SomeToken), ...(ngDevMode ? [{ debugName: "query5" }] : [])); + this.query6 = viewChildren(SomeToken, ...(ngDevMode ? [{ debugName: "query6" }] : [])); this.query7 = viewChild('locatorE', Object.assign(Object.assign({}, (ngDevMode ? { debugName: "query7" } : {})), { read: SomeToken })); this.query8 = contentChildren('locatorF, locatorG', Object.assign(Object.assign({}, (ngDevMode ? { debugName: "query8" } : {})), { descendants: true })); this.query9 = contentChildren(nonAnalyzableRefersToString, Object.assign(Object.assign({}, (ngDevMode ? { debugName: "query9" } : {})), { descendants: true })); @@ -53,10 +53,10 @@ import { Component, contentChild, contentChildren, viewChild, viewChildren } fro import * as i0 from "@angular/core"; export class TestComp { constructor() { - this.query1 = viewChild('locatorA', Object.assign({}, (ngDevMode ? { debugName: "query1" } : {}))); - this.query2 = viewChildren('locatorB', Object.assign({}, (ngDevMode ? { debugName: "query2" } : {}))); - this.query3 = contentChild('locatorC', Object.assign({}, (ngDevMode ? { debugName: "query3" } : {}))); - this.query4 = contentChildren('locatorD', Object.assign({}, (ngDevMode ? { debugName: "query4" } : {}))); + this.query1 = viewChild('locatorA', ...(ngDevMode ? [{ debugName: "query1" }] : [])); + this.query2 = viewChildren('locatorB', ...(ngDevMode ? [{ debugName: "query2" }] : [])); + this.query3 = contentChild('locatorC', ...(ngDevMode ? [{ debugName: "query3" }] : [])); + this.query4 = contentChildren('locatorD', ...(ngDevMode ? [{ debugName: "query4" }] : [])); } } TestComp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, deps: [], target: i0.ɵɵFactoryTarget.Component }); @@ -88,8 +88,8 @@ import { ContentChild, contentChild, Directive, ViewChild, viewChild } from '@an import * as i0 from "@angular/core"; export class TestDir { constructor() { - this.signalViewChild = viewChild('locator1', Object.assign({}, (ngDevMode ? { debugName: "signalViewChild" } : {}))); - this.signalContentChild = contentChild('locator2', Object.assign({}, (ngDevMode ? { debugName: "signalContentChild" } : {}))); + this.signalViewChild = viewChild('locator1', ...(ngDevMode ? [{ debugName: "signalViewChild" }] : [])); + this.signalContentChild = contentChild('locator2', ...(ngDevMode ? [{ debugName: "signalContentChild" }] : [])); } } TestDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_component.js b/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_component.js index 77e1de066b91..26f5c0939e36 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_component.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_component.js @@ -2,18 +2,16 @@ TestComp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … contentQueries: function TestComp_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query3, _c0, 5); - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query4, _c1, 4); + $r3$.ɵɵcontentQuerySignal(dirIndex, ctx.query3, _c0, 5)(dirIndex, ctx.query4, _c1, 4); } if (rf & 2) { - i0.ɵɵqueryAdvance(2); + $r3$.ɵɵqueryAdvance(2); } }, viewQuery: function TestComp_Query(rf, ctx) { if (rf & 1) { - i0.ɵɵviewQuerySignal(ctx.query1, _c2, 5); - i0.ɵɵviewQuerySignal(ctx.query2, _c3, 5); + $r3$.ɵɵviewQuerySignal(ctx.query1, _c2, 5)(ctx.query2, _c3, 5); } if (rf & 2) { - i0.ɵɵqueryAdvance(2); + $r3$.ɵɵqueryAdvance(2); } }, … diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_directive.js b/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_directive.js index 2da03eadfbfd..fd260a1f7bd1 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_directive.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_queries/query_in_directive.js @@ -11,23 +11,16 @@ TestDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ … contentQueries: function TestDir_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query3, _c0, 5); - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query4, _c1, 4); - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query8, _c2, 5); - i0.ɵɵcontentQuerySignal(dirIndex, ctx.query9, nonAnalyzableRefersToString, 5); + $r3$.ɵɵcontentQuerySignal(dirIndex, ctx.query3, _c0, 5)(dirIndex, ctx.query4, _c1, 4)(dirIndex, ctx.query8, _c2, 5)(dirIndex, ctx.query9, nonAnalyzableRefersToString, 5); } if (rf & 2) { - i0.ɵɵqueryAdvance(4); + $r3$.ɵɵqueryAdvance(4); } }, viewQuery: function TestDir_Query(rf, ctx) { if (rf & 1) { - i0.ɵɵviewQuerySignal(ctx.query1, _c3, 5); - i0.ɵɵviewQuerySignal(ctx.query2, _c4, 5); - i0.ɵɵviewQuerySignal(ctx.query5, SomeToken, 5); - i0.ɵɵviewQuerySignal(ctx.query6, SomeToken, 5); - i0.ɵɵviewQuerySignal(ctx.query7, _c5, 5, SomeToken); + $r3$.ɵɵviewQuerySignal(ctx.query1, _c3, 5)(ctx.query2, _c4, 5)(ctx.query5, SomeToken, 5)(ctx.query6, SomeToken, 5)(ctx.query7, _c5, 5, SomeToken); } if (rf & 2) { - i0.ɵɵqueryAdvance(5); + $r3$.ɵɵqueryAdvance(5); } } … diff --git a/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts index 1835642d7730..9d5f20de8ebc 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts @@ -63,8 +63,9 @@ runInEachFileSystem(() => { env.driveMain(); const js = env.getContents('test.js'); - expect(js).toContain(`i0.ɵɵviewQuerySignal(ctx.el, _c0, 5, X);`); - expect(js).toContain(`i0.ɵɵviewQuerySignal(ctx.el2, _c0, 5, fromOtherFile.X);`); + expect(js).toContain( + `i0.ɵɵviewQuerySignal(ctx.el, _c0, 5, X)(ctx.el2, _c0, 5, fromOtherFile.X);`, + ); expect(js).toContain(`i0.ɵɵqueryAdvance(2);`); }); diff --git a/packages/compiler-cli/test/ngtsc/debug_transform_spec.ts b/packages/compiler-cli/test/ngtsc/debug_transform_spec.ts index 046aaa479194..ce77e2220257 100644 --- a/packages/compiler-cli/test/ngtsc/debug_transform_spec.ts +++ b/packages/compiler-cli/test/ngtsc/debug_transform_spec.ts @@ -70,7 +70,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : {}) })`, + `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : []))`, ); }); @@ -88,7 +88,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('signal("Hello World", {})'); + expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -160,7 +160,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : {}) })`, + `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : [])`, ); }); @@ -183,7 +183,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('signal("Hello World", {})'); + expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -277,7 +277,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : {}) })`, + `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : [])`, ); }); @@ -303,7 +303,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('signal("Hello World", {})'); + expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -411,7 +411,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `computed(() => testSignal(), { ...(ngDevMode ? { debugName: "testComputed" } : {}) })`, + `computed(() => testSignal(), ...(ngDevMode ? [{ debugName: "testComputed" }] : []))`, ); }); @@ -429,7 +429,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('computed(() => testSignal(), {})'); + expect(builtContent).toContain('computed(() => testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -526,7 +526,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('computed(() => this.testSignal(), {})'); + expect(builtContent).toContain('computed(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -648,7 +648,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('computed(() => this.testSignal(), {})'); + expect(builtContent).toContain('computed(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -800,10 +800,10 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `model('Hello World', { ...(ngDevMode ? { debugName: "testModel" } : {}) })`, + `model('Hello World', ...(ngDevMode ? [{ debugName: "testModel" }] : [])`, ); expect(jsContents).toContain( - `model(undefined, { ...(ngDevMode ? { debugName: "testModel2" } : {}) })`, + `model(...(ngDevMode ? [undefined, { debugName: "testModel2" }] : [])`, ); }); @@ -824,7 +824,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('model("Hello World", {})'); + expect(builtContent).toContain('model("Hello World")'); }); describe('.required', () => { @@ -845,7 +845,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `model.required({ ...(ngDevMode ? { debugName: "testModel" } : {}) })`, + `model.required(...(ngDevMode ? [{ debugName: "testModel" }] : [])`, ); }); @@ -887,7 +887,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('model.required({});'); + expect(builtContent).toContain('model.required();'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -990,7 +990,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `input(undefined, { ...(ngDevMode ? { debugName: "testInput" } : {}) })`, + `input(...(ngDevMode ? [undefined, { debugName: "testInput" }] : [])`, ); }); @@ -1012,7 +1012,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('input(void 0, {});'); + expect(builtContent).toContain('input();'); }); describe('.required', () => { @@ -1033,7 +1033,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `input.required({ ...(ngDevMode ? { debugName: "testInput" } : {}) })`, + `input.required(...(ngDevMode ? [{ debugName: "testInput" }] : []))`, ); }); @@ -1076,7 +1076,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('input.required({});'); + expect(builtContent).toContain('input.required();'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1191,10 +1191,10 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `viewChild('foo', { ...(ngDevMode ? { debugName: "testViewChild" } : {}) })`, + `viewChild('foo', ...(ngDevMode ? [{ debugName: "testViewChild" }] : [])`, ); expect(jsContents).toContain( - `viewChild(ChildComponent, { ...(ngDevMode ? { debugName: "testViewChildComponent" } : {}) })`, + `viewChild(ChildComponent, ...(ngDevMode ? [{ debugName: "testViewChildComponent" }] : [])`, ); }); @@ -1224,8 +1224,8 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain(`viewChild("foo", {})`); - expect(builtContent).toContain(`viewChild(ChildComponent, {})`); + expect(builtContent).toContain(`viewChild("foo")`); + expect(builtContent).toContain(`viewChild(ChildComponent)`); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1340,7 +1340,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `viewChildren('foo', { ...(ngDevMode ? { debugName: "testViewChildren" } : {}) })`, + `viewChildren('foo', ...(ngDevMode ? [{ debugName: "testViewChildren" }] : [])`, ); }); @@ -1362,7 +1362,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('viewChildren("foo", {})'); + expect(builtContent).toContain('viewChildren("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1466,7 +1466,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `contentChild('foo', { ...(ngDevMode ? { debugName: "testContentChild" } : {}) })`, + `contentChild('foo', ...(ngDevMode ? [{ debugName: "testContentChild" }] : [])`, ); }); @@ -1488,7 +1488,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('contentChild("foo", {})'); + expect(builtContent).toContain('contentChild("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1594,7 +1594,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `contentChildren('foo', { ...(ngDevMode ? { debugName: "testContentChildren" } : {}) })`, + `contentChildren('foo', ...(ngDevMode ? [{ debugName: "testContentChildren" }] : [])`, ); }); @@ -1616,7 +1616,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('contentChildren("foo", {})'); + expect(builtContent).toContain('contentChildren("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1724,7 +1724,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `effect(() => this.testSignal(), { ...(ngDevMode ? { debugName: "testEffect" } : {}) })`, + `effect(() => this.testSignal(), ...(ngDevMode ? [{ debugName: "testEffect" }] : [])`, ); }); @@ -1745,7 +1745,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('effect(() => this.testSignal(), {})'); + expect(builtContent).toContain('effect(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -1845,7 +1845,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( - `linkedSignal(() => testSignal(), { ...(ngDevMode ? { debugName: "testLinkedSignal" } : {}) })`, + `linkedSignal(() => testSignal(), ...(ngDevMode ? [{ debugName: "testLinkedSignal" }] : [])`, ); }); @@ -1863,7 +1863,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('linkedSignal(() => testSignal(), {})'); + expect(builtContent).toContain('linkedSignal(() => testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -2039,7 +2039,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('linkedSignal(() => this.testSignal(), {})'); + expect(builtContent).toContain('linkedSignal(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -2251,7 +2251,7 @@ runInEachFileSystem(() => { const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); - expect(builtContent).toContain('linkedSignal(() => this.testSignal(), {})'); + expect(builtContent).toContain('linkedSignal(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -2777,9 +2777,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( - `httpResource(() => '/api', { ` + - '...(ngDevMode ? { debugName: "testHttpResource" } : {}) ' + - '})', + `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : []))`, ); }); @@ -2798,7 +2796,7 @@ runInEachFileSystem(() => { const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); - expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api", {})`); + expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -2839,9 +2837,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( - `httpResource(() => '/api', { ` + - '...(ngDevMode ? { debugName: "testHttpResource" } : {}) ' + - '})', + `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : []))`, ); }); @@ -2864,7 +2860,7 @@ runInEachFileSystem(() => { const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); - expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api", {})`); + expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { @@ -2913,9 +2909,7 @@ runInEachFileSystem(() => { env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( - `httpResource(() => '/api', { ` + - '...(ngDevMode ? { debugName: "testHttpResource" } : {}) ' + - '})', + `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : []))`, ); }); @@ -2941,7 +2935,7 @@ runInEachFileSystem(() => { const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); - expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api", {})`); + expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { diff --git a/packages/compiler-cli/test/ngtsc/doc_extraction/decorator_doc_extraction_spec.ts b/packages/compiler-cli/test/ngtsc/doc_extraction/decorator_doc_extraction_spec.ts index 480642960d7f..7a79513f9bcb 100644 --- a/packages/compiler-cli/test/ngtsc/doc_extraction/decorator_doc_extraction_spec.ts +++ b/packages/compiler-cli/test/ngtsc/doc_extraction/decorator_doc_extraction_spec.ts @@ -177,5 +177,51 @@ runInEachFileSystem(() => { expect(param1.type).toBe('string'); expect(param1.description).toBe(''); }); + + it('should extract the decorator with some deprecated signatures', () => { + env.write( + 'index.ts', + ` + export interface InjectableDecorator { + /** + * Decorator that marks a class as available to be + * provided and injected as a dependency. + * + * @usageNotes + * + * Some usage.... + */ + (): TypeDecorator; + /** + * @deprecated deprecated. + */ + (options?: {providedIn: Type | 'any'} & InjectableProvider): TypeDecorator; + ( + options?: {providedIn: Type | 'root' | 'platform' | 'any' | null} & InjectableProvider, + ): TypeDecorator; + new (): Injectable; + new ( + options?: {providedIn: Type | 'root' | 'platform' | 'any' | null} & InjectableProvider, + ): Injectable; + } + + export interface Injectable { + providedIn?: Type | 'root' | 'platform' | 'any' | null; + } + + function makePropDecorator(): InjectDecorator { return () => {}; } + export const Injectable: InjectableDecorator = makeDecorator('Injectable', undefined, undefined, undefined,(type: Type, meta: Injectable) => ({} as any), + ); + `, + ); + + const docs: DocEntry[] = env.driveDocsExtraction('index.ts'); + expect(docs.length).toBe(1); + + expect(docs[0].name).toBe('Injectable'); + expect(docs[0].description).toContain('Decorator that marks a class'); + expect(docs[0].entryType).toBe(EntryType.Decorator); + expect(docs[0].jsdocTags[0]).toBeDefined(); + }); }); }); diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 8934e2259457..e8b4cc69ac2a 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -27,14 +27,28 @@ const trim = (input: string): string => input.replace(/\s+/g, ' ').trim(); const varRegExp = (name: string): RegExp => new RegExp(`const \\w+ = \\[\"${name}\"\\];`); -const viewQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => { - const maybeRef = ref ? `, ${ref}` : ``; - return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${flags}${maybeRef}\\)`); +const viewQueryRegExp = (queries: [{predicate: string; flags: number; ref?: string}]): RegExp => { + let result = `i0\\.ɵɵviewQuery`; + + for (const {ref, predicate, flags} of queries) { + const maybeRef = ref ? `, ${ref}` : ``; + result += `\\(${predicate}, ${flags}${maybeRef}\\)`; + } + + return new RegExp(result); }; -const contentQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => { - const maybeRef = ref ? `, ${ref}` : ``; - return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${flags}${maybeRef}\\)`); +const contentQueryRegExp = ( + queries: {predicate: string; flags: number; ref?: string}[], +): RegExp => { + let result = `i0\\.ɵɵcontentQuery`; + + for (const {predicate, flags, ref} of queries) { + const maybeRef = ref ? `, ${ref}` : ``; + result += `\\(dirIndex, ${predicate}, ${flags}${maybeRef}\\)`; + } + + return new RegExp(result); }; const setClassMetadataRegExp = (expectedType: string): RegExp => @@ -5232,9 +5246,11 @@ runInEachFileSystem((os: string) => { expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); + expect(jsContents).toMatch( + contentQueryRegExp([{predicate: '\\w+', flags: 5, ref: 'TemplateRef'}]), + ); // match `i0.ɵɵviewQuery(_c2, 5, null)` - expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); + expect(jsContents).toMatch(viewQueryRegExp([{predicate: '\\w+', flags: 5}])); }); it('should generate queries for directives', () => { @@ -5267,13 +5283,15 @@ runInEachFileSystem((os: string) => { expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); + expect(jsContents).toMatch( + contentQueryRegExp([{predicate: '\\w+', flags: 5, ref: 'TemplateRef'}]), + ); // match `i0.ɵɵviewQuery(_c2, 5)` // Note that while ViewQuery doesn't necessarily make sense on a directive, // because it doesn't have a view, we still need to handle it because a component // could extend the directive. - expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); + expect(jsContents).toMatch(viewQueryRegExp([{predicate: '\\w+', flags: 5}])); }); it('should handle queries that use forwardRef', () => { @@ -5298,13 +5316,15 @@ runInEachFileSystem((os: string) => { env.driveMain(); const jsContents = env.getContents('test.js'); - // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 5, null)` - expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', 5)); - // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, 5, null)` - expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', 5)); - // match `i0.ɵɵcontentQuery(dirIndex, _c0, 5, null)` + // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 5, null)(dirIndex, ViewContainerRef, 5, null)(dirIndex, _c0, 5, null)` + expect(jsContents).toMatch( + contentQueryRegExp([ + {predicate: 'TemplateRef', flags: 5}, + {predicate: 'ViewContainerRef', flags: 5}, + {predicate: '_c0', flags: 5}, + ]), + ); expect(jsContents).toContain('_c0 = ["parens"];'); - expect(jsContents).toMatch(contentQueryRegExp('_c0', 5)); }); it('should handle queries that use an InjectionToken', () => { @@ -5329,9 +5349,9 @@ runInEachFileSystem((os: string) => { env.driveMain(); const jsContents = env.getContents('test.js'); // match `i0.ɵɵviewQuery(TOKEN, 5, null)` - expect(jsContents).toMatch(viewQueryRegExp('TOKEN', 5)); + expect(jsContents).toMatch(viewQueryRegExp([{predicate: 'TOKEN', flags: 5}])); // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, 5, null)` - expect(jsContents).toMatch(contentQueryRegExp('TOKEN', 5)); + expect(jsContents).toMatch(contentQueryRegExp([{predicate: 'TOKEN', flags: 5}])); }); it('should compile expressions that write keys', () => { @@ -8477,7 +8497,7 @@ runInEachFileSystem((os: string) => { hostVars: 6, hostBindings: function UnsafeAttrsDirective_HostBindings(rf, ctx) { if (rf & 2) { - i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("profile", ctx.attrProfile, i0.ɵɵsanitizeResourceUrl)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle); + i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("profile", ctx.attrProfile)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle); } } `; @@ -9258,6 +9278,33 @@ runInEachFileSystem((os: string) => { }); }); + describe('SVG animation processing', () => { + it('should generate SVG animation validation instruction', () => { + env.write( + 'test.ts', + ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test-cmp', + template: '', + standalone: false, + }) + export class TestCmp { + attr = 'opacity'; + } + `, + ); + + env.driveMain(); + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain( + 'i0.ɵɵattribute("attributeName", ctx.attr, i0.ɵɵvalidateAttribute);', + ); + }); + }); + describe('inline resources', () => { it('should process inline -
    - `, + +
    + `, styles: ['div { width: 100px; }'], standalone: false, }) @@ -3486,13 +3500,12 @@ describe('styling', () => { it('should allow multiple styling bindings to work alongside property/attribute bindings', () => { @Component({ - template: ` -
    -
    `, + template: `
    `, standalone: false, }) class MyComp {} @@ -3639,12 +3652,13 @@ describe('styling', () => { @Component({ template: ` - - + + `, standalone: false, }) @@ -3737,14 +3751,9 @@ describe('styling', () => { @Component({ template: ` -
    -
    - `, +
    +
    + `, standalone: false, }) class MyComp { @@ -3965,7 +3974,11 @@ describe('styling', () => { @Component({ template: `Hello`, - styles: `span {font-size: 10px}`, + styles: ` + span { + font-size: 10px; + } + `, standalone: false, }) class Cmp {} @@ -4071,7 +4084,7 @@ describe('styling', () => { } @Component({ - template: ``, + template: ``, standalone: false, }) class MyApp { @@ -4096,7 +4109,7 @@ describe('styling', () => { it('should not bind [class] to @Input("className")', () => { @Component({ selector: 'my-cmp', - template: `className = {{className}}`, + template: `className = {{ className }}`, standalone: false, }) class MyCmp { @@ -4117,7 +4130,7 @@ describe('styling', () => { it('should not bind class to @Input("className")', () => { @Component({ selector: 'my-cmp', - template: `className = {{className}}`, + template: `className = {{ className }}`, standalone: false, }) class MyCmp { diff --git a/packages/core/test/acceptance/template_ref_spec.ts b/packages/core/test/acceptance/template_ref_spec.ts index f8694c4b8b07..c8ccdc926b8e 100644 --- a/packages/core/test/acceptance/template_ref_spec.ts +++ b/packages/core/test/acceptance/template_ref_spec.ts @@ -57,11 +57,11 @@ describe('TemplateRef', () => { @Component({ selector: 'menu-content', template: ` - - Header - - - `, + + Header + + + `, exportAs: 'menuContent', standalone: false, }) @@ -71,12 +71,12 @@ describe('TemplateRef', () => { @Component({ template: ` - - - - - - `, + + + + + + `, standalone: false, }) class App { @@ -271,9 +271,9 @@ describe('TemplateRef', () => { describe('context', () => { @Component({ template: ` - {{name}} - - `, + {{ name }} + + `, standalone: false, }) class App { diff --git a/packages/core/test/acceptance/text_spec.ts b/packages/core/test/acceptance/text_spec.ts index ba00996049ca..b508ea409255 100644 --- a/packages/core/test/acceptance/text_spec.ts +++ b/packages/core/test/acceptance/text_spec.ts @@ -11,6 +11,7 @@ import {of} from 'rxjs'; describe('text instructions', () => { it('should handle all flavors of interpolated text', () => { + // prettier-ignore @Component({ template: `
    a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j
    @@ -61,6 +62,7 @@ describe('text instructions', () => { }); it('should handle piped values in interpolated text', () => { + // prettier-ignore @Component({ template: `

    {{who | async}} sells {{(item | async)?.what}} down by the {{(item | async)?.where}}.

    diff --git a/packages/core/test/acceptance/view_container_ref_spec.ts b/packages/core/test/acceptance/view_container_ref_spec.ts index 9e9f093a0778..ce27b86aa842 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -105,7 +105,10 @@ describe('ViewContainerRef', () => { it('should construct proper TNode / DOM tree when embedded views are created in a directive constructor', () => { @Component({ selector: 'view-insertion-test-cmpt', - template: `
    before|middle|after
    `, + template: `
    + before|middle|after +
    `, standalone: false, }) class ViewInsertionTestCmpt {} @@ -113,7 +116,7 @@ describe('ViewContainerRef', () => { TestBed.configureTestingModule({declarations: [ViewInsertionTestCmpt, ConstructorDir]}); const fixture = TestBed.createComponent(ViewInsertionTestCmpt); - expect(fixture.nativeElement).toHaveText('before|middle|after'); + expect(fixture.nativeElement).toHaveText(' before|middle|after '); }); it('should use comment node of host ng-container as insertion marker', () => { @@ -124,9 +127,7 @@ describe('ViewContainerRef', () => { class HelloComp {} @Component({ - template: ` - - `, + template: ` `, standalone: false, }) class TestComp { @@ -172,9 +173,7 @@ describe('ViewContainerRef', () => { class HelloComp {} @Component({ - template: ` - - `, + template: ` `, standalone: false, }) class TestComp { @@ -799,7 +798,7 @@ describe('ViewContainerRef', () => { it('should work on templates', () => { @Component({ template: ` - {{name}} + {{ name }}
    `, standalone: false, @@ -1054,11 +1053,9 @@ describe('ViewContainerRef', () => { template: `
    I host a template
    -
    I host a child template
    -
    - - I am child template +
    I host a child template
    + I am child template `, standalone: false, }) @@ -1113,10 +1110,10 @@ describe('ViewContainerRef', () => { it('should work on elements', () => { @Component({ template: ` - {{name}} -
    -
    - `, + {{ name }} +
    +
    + `, standalone: false, }) class TestComponent {} @@ -1166,10 +1163,10 @@ describe('ViewContainerRef', () => { @Component({ template: ` - {{name}} + {{ name }}
    - `, + `, standalone: false, }) class TestComponent {} @@ -1213,7 +1210,7 @@ describe('ViewContainerRef', () => { it('should work with multiple instances of view container refs', () => { @Component({ template: ` - {{name}} + {{ name }}
    `, @@ -1243,7 +1240,7 @@ describe('ViewContainerRef', () => { it('should work on templates', () => { @Component({ template: ` - {{name}} + {{ name }}
    `, standalone: false, @@ -1278,7 +1275,7 @@ describe('ViewContainerRef', () => { it('should apply directives and pipes of the host view to the TemplateRef', () => { @Component({ selector: 'child', - template: `{{name}}`, + template: `{{ name }}`, standalone: false, }) class Child { @@ -1297,12 +1294,12 @@ describe('ViewContainerRef', () => { @Component({ template: ` - - - - - - `, + + + + + + `, standalone: false, }) class SomeComponent {} @@ -1495,7 +1492,9 @@ describe('ViewContainerRef', () => { it('should support reprojection of projectable nodes', () => { @Component({ selector: 'reprojector', - template: ``, + template: ``, standalone: false, }) class Reprojector {} @@ -1975,7 +1974,7 @@ describe('ViewContainerRef', () => { it('should work with a template declared in a different component view from insertion', () => { @Component({ selector: 'child', - template: `
    {{name}}
    `, + template: `
    {{ name }}
    `, standalone: false, }) class Child { @@ -1986,7 +1985,7 @@ describe('ViewContainerRef', () => { @Component({ template: ` -
    {{name}}
    +
    {{ name }}
    @@ -2018,10 +2017,7 @@ describe('ViewContainerRef', () => { it('should work with nested for loops with different declaration / insertion points', () => { @Component({ selector: 'loop-comp', - template: ` - - - `, + template: ` `, standalone: false, }) class LoopComp { @@ -2034,7 +2030,7 @@ describe('ViewContainerRef', () => { template: ` -
    {{cell}} - {{row.value}} - {{name}}
    +
    {{ cell }} - {{ row.value }} - {{ name }}
    @@ -2081,9 +2077,7 @@ describe('ViewContainerRef', () => { it('should insert elements in the proper order when template root is an ng-container', () => { @Component({ - template: ` - |{{ item }}| - `, + template: ` |{{ item }}| `, standalone: false, }) class App { @@ -2120,10 +2114,10 @@ describe('ViewContainerRef', () => { it('should insert elements in the proper order when template root is an ng-container and is wrapped by an ng-container', () => { @Component({ template: ` - - |{{ item }}| - - `, + + |{{ item }}| + + `, standalone: false, }) class App { @@ -2160,8 +2154,10 @@ describe('ViewContainerRef', () => { it('should insert elements in the proper order when template root is an ng-container and first node is a ng-container', () => { @Component({ template: ` - |{{ item }}| - `, + |{{ item }}| + `, standalone: false, }) class App { @@ -2198,10 +2194,12 @@ describe('ViewContainerRef', () => { it('should insert elements in the proper order when template root is an ng-container, wrapped in an ng-container with the root node as an ng-container', () => { @Component({ template: ` - - |{{ item }}| - - `, + + |{{ item }}| + + `, standalone: false, }) class App { @@ -2239,7 +2237,9 @@ describe('ViewContainerRef', () => { it('should insert elements in the proper order when the first child node is an ICU expression', () => { @Component({ template: ` - {count, select, other {|{{ item }}|}} + {count, select, + other {|{{ item }}|} + } `, standalone: false, }) @@ -2281,7 +2281,7 @@ describe('ViewContainerRef', () => { @Component({ selector: 'hooks', - template: `{{name}}`, + template: `{{ name }}`, standalone: false, }) class ComponentWithHooks { @@ -2663,9 +2663,8 @@ describe('ViewContainerRef', () => { @Component({ selector: 'parent', - template: ` - - {{name}} + template: ` + {{ name }} @@ -2707,9 +2706,8 @@ describe('ViewContainerRef', () => { @Component({ selector: 'parent', - template: ` - - {{name}} + template: ` + {{ name }} Before projected @@ -2753,7 +2751,10 @@ describe('ViewContainerRef', () => { @Component({ selector: 'my-app', - template: `
    `, + template: ` +
    `, standalone: false, }) class MyApp { @@ -2773,8 +2774,7 @@ describe('ViewContainerRef', () => { describe('with select', () => { @Component({ selector: 'child-with-selector', - template: ` -

    + template: `

    `, standalone: false, }) @@ -2785,7 +2785,7 @@ describe('ViewContainerRef', () => { selector: 'parent', template: ` - {{name}} + {{ name }}
    blah
    @@ -2827,12 +2827,12 @@ describe('ViewContainerRef', () => { @Component({ selector: 'my-comp', template: ` - -
    -
    + +
    +
    - My Content - `, + My Content + `, standalone: false, }) class MyComp { @@ -2856,7 +2856,7 @@ describe('ViewContainerRef', () => { selector: 'parent', template: ` - {{name}} + {{ name }}
    blah
    @@ -2924,7 +2924,7 @@ describe('ViewContainerRef', () => { it('should check bindings for components dynamically created by root component', () => { @Component({ selector: 'dynamic-cmpt-with-bindings', - template: `check count: {{checkCount}}`, + template: `check count: {{ checkCount }}`, standalone: false, }) class DynamicCompWithBindings implements DoCheck { @@ -2980,7 +2980,7 @@ describe('ViewContainerRef', () => { @Component({ selector: 'child', - template: `
    {{name}}
    `, + template: `
    {{ name }}
    `, standalone: false, }) class Child { @@ -3027,7 +3027,7 @@ describe('ViewContainerRef', () => { @Component({ template: ` - {{name}} + {{ name }}

    `, standalone: false, @@ -3060,7 +3060,9 @@ class VCRefDirective { @Component({ selector: `embedded-cmp-with-ngcontent`, - template: `
    `, + template: ` +
    + `, standalone: false, }) class EmbeddedComponentWithNgContent {} @@ -3082,9 +3084,7 @@ class ViewContainerRefComp { @Component({ selector: 'view-container-ref-app', - template: ` - - `, + template: ` `, standalone: false, }) class ViewContainerRefApp { @@ -3112,7 +3112,7 @@ export class StructDir { @Component({ selector: 'destroy-cases', - template: ` `, + template: ``, standalone: false, }) class DestroyCasesComp { diff --git a/packages/core/test/acceptance/view_insertion_spec.ts b/packages/core/test/acceptance/view_insertion_spec.ts index 4bbedd604fad..333f17e5d466 100644 --- a/packages/core/test/acceptance/view_insertion_spec.ts +++ b/packages/core/test/acceptance/view_insertion_spec.ts @@ -36,7 +36,7 @@ describe('view insertion', () => { @Component({ selector: 'increment-comp', - template: `created{{counter}}`, + template: `created{{ counter }}`, standalone: false, }) class IncrementComp { @@ -45,9 +45,9 @@ describe('view insertion', () => { @Component({ template: ` - -
    - `, + +
    + `, standalone: false, }) class App { @@ -103,9 +103,9 @@ describe('view insertion', () => { it('should insert into an empty container, at the front, in the middle, and at the end', () => { @Component({ template: ` - -
    - `, + +
    + `, standalone: false, }) class App { @@ -152,9 +152,9 @@ describe('view insertion', () => { @Component({ selector: 'comp', template: ` - -
    - `, + +
    + `, standalone: false, }) class Comp { @@ -183,9 +183,7 @@ describe('view insertion', () => { } @Component({ - template: ` - test - `, + template: ` test `, standalone: false, }) class App {} @@ -212,9 +210,9 @@ describe('view insertion', () => { it('should insert into an empty container, at the front, in the middle, and at the end', () => { @Component({ template: ` -
    test
    -
    - `, +
    test
    +
    + `, standalone: false, }) class App { @@ -535,9 +533,9 @@ describe('view insertion', () => { @Component({ selector: 'test-cmpt', template: ` - insert -
    - `, + insert +
    + `, standalone: false, }) class TestCmpt { @@ -588,11 +586,11 @@ describe('view insertion', () => { @Component({ selector: 'app-root', template: ` -
    start|
    - -
    |end
    +
    start|
    + +
    |end
    -
    |click
    +
    |click
    `, standalone: false, }) @@ -621,12 +619,12 @@ describe('view insertion', () => { @Component({ selector: 'app-root', template: ` -
    container start|
    - -
    |container end
    +
    container start|
    + +
    |container end
    - test -
    |click
    + test +
    |click
    `, standalone: false, }) @@ -657,11 +655,13 @@ describe('view insertion', () => { selector: 'app-root', template: ` - {{parameter}} + {{ parameter }} - + [ngTemplateOutletContext]="{parameter: parameter}" + > `, standalone: false, @@ -874,7 +874,9 @@ describe('view insertion', () => { } @Component({ - template: ``, + template: ``, standalone: false, }) class App {} @@ -908,7 +910,7 @@ describe('view insertion', () => { } @Component({ - template: `
    {{value}}
    `, + template: `
    {{ value }}
    `, standalone: false, }) class TestCmpt { diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 20be538f99e9..6b54b8fa0e0e 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -260,9 +260,7 @@ describe('bootstrap', () => { it('runs in `NgZone`', inject([ApplicationRef], async (ref: ApplicationRef) => { @Component({ selector: 'zone-comp', - template: ` -
    {{ name }}
    - `, + template: `
    {{ name }}
    `, }) class ZoneComp { readonly inNgZone = NgZone.isInAngularZone(); @@ -856,7 +854,7 @@ describe('AppRef', () => { describe('stability', () => { @Component({ selector: 'sync-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class SyncComp { @@ -865,7 +863,7 @@ describe('AppRef', () => { @Component({ selector: 'click-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class ClickComp { @@ -878,7 +876,7 @@ describe('AppRef', () => { @Component({ selector: 'micro-task-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class MicroTaskComp { @@ -893,7 +891,7 @@ describe('AppRef', () => { @Component({ selector: 'macro-task-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class MacroTaskComp { @@ -908,7 +906,7 @@ describe('AppRef', () => { @Component({ selector: 'micro-macro-task-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class MicroMacroTaskComp { @@ -926,7 +924,7 @@ describe('AppRef', () => { @Component({ selector: 'macro-micro-task-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class MacroMicroTaskComp { diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index 951f301d8a97..d2a5a9fa5ee0 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -7,6 +7,7 @@ "ANIMATION_PREFIX", "ANIMATION_QUEUE", "ANY_STATE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -66,6 +67,7 @@ "ComponentRef2", "ConsumerObserver", "DASH_CASE_REGEXP", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -226,6 +228,7 @@ "SHARED_ANIMATION_PROVIDERS", "SIGNAL", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "STAR_CLASSNAME", "STAR_SELECTOR", "SUBSTITUTION_EXPR_END", @@ -339,6 +342,7 @@ "areAnimationSupported", "arrRemove", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "balancePreviousStylesIntoKeyframes", "balanceProperties", @@ -858,4 +862,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/animations-standalone/main.ts b/packages/core/test/bundling/animations-standalone/main.ts index 7bf7c3473f9e..7b2802fcc18f 100644 --- a/packages/core/test/bundling/animations-standalone/main.ts +++ b/packages/core/test/bundling/animations-standalone/main.ts @@ -12,9 +12,7 @@ import {provideAnimations} from '@angular/platform-browser/animations'; @Component({ selector: 'app-animations', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])]), ], @@ -25,9 +23,7 @@ class AnimationsComponent { @Component({ selector: 'app-root', - template: ` - - `, + template: ` `, imports: [AnimationsComponent], }) class RootComponent {} diff --git a/packages/core/test/bundling/create_component/bundle.golden_symbols.json b/packages/core/test/bundling/create_component/bundle.golden_symbols.json index 20e5657ff58a..e60602de7b05 100644 --- a/packages/core/test/bundling/create_component/bundle.golden_symbols.json +++ b/packages/core/test/bundling/create_component/bundle.golden_symbols.json @@ -4,6 +4,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -42,6 +43,7 @@ "ComponentRef", "ComponentRef2", "ConsumerObserver", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -78,6 +80,7 @@ "EventEmitter_", "EventManager", "EventManagerPlugin", + "FIELD_BINDING_METADATA", "FLAGS", "FieldComponent", "FieldDirective", @@ -173,6 +176,7 @@ "SIGNAL", "SIGNAL_NODE", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "Sanitizer", @@ -202,6 +206,7 @@ "XhrFactory", "ZONELESS_ENABLED", "ZoneAwareEffectScheduler", + "\\u0275CONTROL", "\\u0275\\u0275defineComponent", "\\u0275\\u0275defineDirective", "\\u0275\\u0275defineInjectable", @@ -263,6 +268,7 @@ "areAnimationSupported", "arrRemove", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -293,6 +299,7 @@ "consumerMarkDirty", "consumerPollProducersForChange", "context", + "controlBinding", "convertToBitFlags", "convertToInjectOptions", "couldBeInjectableType", @@ -686,4 +693,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 900b22feb46a..9faceefd656d 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -61,6 +61,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_INITIALIZER", @@ -93,6 +94,7 @@ "ComponentRef", "ComponentRef2", "ConsumerObserver", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -218,6 +220,7 @@ "SIMPLE_CHANGES_STORE", "SSR_BLOCK_STATE", "SSR_UNIQUE_ID", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "Sanitizer", @@ -310,6 +313,7 @@ "areAnimationSupported", "arrRemove", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "bind", "bindingUpdated", @@ -744,4 +748,4 @@ "writeToDirectiveInput" ] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/defer/defer.component.ts b/packages/core/test/bundling/defer/defer.component.ts index 0c4444922e5e..cc807af0b6da 100644 --- a/packages/core/test/bundling/defer/defer.component.ts +++ b/packages/core/test/bundling/defer/defer.component.ts @@ -10,8 +10,6 @@ import {Component} from '@angular/core'; @Component({ selector: 'defer-cmp', - template: ` -

    Defer-loaded component

    - `, + template: `

    Defer-loaded component

    `, }) export class DeferComponent {} diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 9313004bfd38..cd8df286076c 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -4,6 +4,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -62,6 +63,7 @@ "ConsumerObserver", "ControlContainer", "ControlEvent", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -229,6 +231,7 @@ "SIGNAL", "SIGNAL_NODE", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "SafeValueImpl", @@ -371,6 +374,7 @@ "assertControlPresent", "assertNotDestroyed", "assertPlatform", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -1018,4 +1022,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/forms_reactive/main.ts b/packages/core/test/bundling/forms_reactive/main.ts index d458ab8ab545..9531b259ec12 100644 --- a/packages/core/test/bundling/forms_reactive/main.ts +++ b/packages/core/test/bundling/forms_reactive/main.ts @@ -84,9 +84,7 @@ class ReactiveFormsComponent { @Component({ selector: 'app-root', - template: ` - - `, + template: ` `, standalone: false, }) class RootComponent {} diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index c7814601060e..4f17ff2f041d 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -4,6 +4,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -63,6 +64,7 @@ "ConsumerObserver", "ControlContainer", "ControlEvent", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -224,6 +226,7 @@ "SIGNAL", "SIGNAL_NODE", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "SafeValueImpl", @@ -375,6 +378,7 @@ "assertControlPresent", "assertNotDestroyed", "assertPlatform", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -1018,4 +1022,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/forms_template_driven/main.ts b/packages/core/test/bundling/forms_template_driven/main.ts index 5a705ba039ce..947e47ebe2d9 100644 --- a/packages/core/test/bundling/forms_template_driven/main.ts +++ b/packages/core/test/bundling/forms_template_driven/main.ts @@ -54,9 +54,7 @@ class TemplateFormsComponent { @Component({ selector: 'app-root', - template: ` - - `, + template: ` `, standalone: false, }) class RootComponent {} diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index df18ccd711c6..ba6f62617343 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -6,6 +6,7 @@ "ALLOWED_METHODS", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -50,6 +51,7 @@ "ComponentRef", "ComponentRef2", "ConsumerObserver", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -202,6 +204,7 @@ "SKIP_HYDRATION_ATTR_NAME", "SKIP_HYDRATION_ATTR_NAME_LOWER_CASE", "SSR_CONTENT_INTEGRITY_MARKER", + "STABILITY_WARNING_THRESHOLD", "STATUS", "STATUS_TEXT", "SVG_NAMESPACE", @@ -305,6 +308,7 @@ "areAnimationSupported", "arrRemove", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -797,4 +801,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/image-directive/e2e/basic/basic.ts b/packages/core/test/bundling/image-directive/e2e/basic/basic.ts index bf676d087195..b9592c772f90 100644 --- a/packages/core/test/bundling/image-directive/e2e/basic/basic.ts +++ b/packages/core/test/bundling/image-directive/e2e/basic/basic.ts @@ -12,7 +12,7 @@ import {Component} from '../../../../../src/core'; @Component({ selector: 'basic', imports: [NgOptimizedImage], - template: ``, + template: ``, providers: [ { provide: IMAGE_LOADER, diff --git a/packages/core/test/bundling/image-directive/e2e/fill-mode/fill-mode.ts b/packages/core/test/bundling/image-directive/e2e/fill-mode/fill-mode.ts index 7fd353f82abf..056a14e45315 100644 --- a/packages/core/test/bundling/image-directive/e2e/fill-mode/fill-mode.ts +++ b/packages/core/test/bundling/image-directive/e2e/fill-mode/fill-mode.ts @@ -15,7 +15,7 @@ import {Component} from '../../../../../src/core'; template: `
    - +
    `, }) @@ -25,7 +25,7 @@ export class FillModePassingComponent {} imports: [NgOptimizedImage], template: `
    - +
    `, }) diff --git a/packages/core/test/bundling/image-directive/e2e/image-distortion/image-distortion.ts b/packages/core/test/bundling/image-directive/e2e/image-distortion/image-distortion.ts index a92db050a6d1..10c6574bc9ef 100644 --- a/packages/core/test/bundling/image-directive/e2e/image-distortion/image-distortion.ts +++ b/packages/core/test/bundling/image-directive/e2e/image-distortion/image-distortion.ts @@ -13,93 +13,127 @@ import {Component} from '../../../../../src/core'; selector: 'image-distortion-passing', imports: [NgOptimizedImage], template: ` - - - -
    - - -
    - - - - -
    - - - - - - - - - -
    - - - -
    - - -
    - - - - -
    - - - - - - - - - -
    - - -
    - `, + + + +
    + + +
    + + + + +
    + + + + + + + + + +
    + + + +
    + + +
    + + + + +
    + + + + + + + + + +
    + + +
    + `, }) export class ImageDistortionPassingComponent {} @Component({ selector: 'image-distortion-failing', imports: [NgOptimizedImage], template: ` - - - -
    - - - - - - - - -
    - -
    - - - - -
    - - - - `, + + + +
    + + + + + + + + +
    + +
    + + + + +
    + + + + `, }) export class ImageDistortionFailingComponent {} diff --git a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-lazy/image-perf-warnings-lazy.ts b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-lazy/image-perf-warnings-lazy.ts index d769e4dbc19c..f54a0105d1f5 100644 --- a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-lazy/image-perf-warnings-lazy.ts +++ b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-lazy/image-perf-warnings-lazy.ts @@ -12,15 +12,15 @@ import {Component} from '../../../../../src/core'; selector: 'image-perf-warnings-lazy', template: ` - + -
    +
    - + `, }) export class ImagePerfWarningsLazyComponent {} diff --git a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/image-perf-warnings-oversized.ts b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/image-perf-warnings-oversized.ts index e257c85b9dd1..86b55ed8a735 100644 --- a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/image-perf-warnings-oversized.ts +++ b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/image-perf-warnings-oversized.ts @@ -11,10 +11,10 @@ import {Component} from '../../../../../src/core'; @Component({ selector: 'image-perf-warnings-oversized', template: ` - -
    - -
    - `, + +
    + +
    + `, }) export class ImagePerfWarningsOversizedComponent {} diff --git a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/svg-no-perf-oversized-warnings.ts b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/svg-no-perf-oversized-warnings.ts index 70a06f058746..2f7d54f148c0 100644 --- a/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/svg-no-perf-oversized-warnings.ts +++ b/packages/core/test/bundling/image-directive/e2e/image-perf-warnings-oversized/svg-no-perf-oversized-warnings.ts @@ -11,10 +11,10 @@ import {Component} from '../../../../../src/core'; @Component({ selector: 'svg-no-perf-oversized-warnings', template: ` - -
    - -
    - `, + +
    + +
    + `, }) export class SvgNoOversizedPerfWarningsComponent {} diff --git a/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.ts b/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.ts index 43dfaa0bc0d6..b3faf9da2cb5 100644 --- a/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.ts +++ b/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.ts @@ -17,20 +17,20 @@ import {Component} from '../../../../../src/core'; 'b.png' should *not* be treated as an LCP element, since there is a bigger one right below it --> - + -
    +
    - + -
    +
    - + `, }) export class LcpCheckComponent { diff --git a/packages/core/test/bundling/image-directive/e2e/oversized-image/oversized-image.ts b/packages/core/test/bundling/image-directive/e2e/oversized-image/oversized-image.ts index 4fc77b12ab21..ac3c351b1c8d 100644 --- a/packages/core/test/bundling/image-directive/e2e/oversized-image/oversized-image.ts +++ b/packages/core/test/bundling/image-directive/e2e/oversized-image/oversized-image.ts @@ -19,16 +19,15 @@ const imageLoader = { imports: [NgOptimizedImage], providers: [imageLoader], template: ` - -
    - -
    - -
    - -
    - `, + +
    + +
    + +
    + +
    + `, }) export class OversizedImageComponentPassing {} @@ -37,10 +36,10 @@ export class OversizedImageComponentPassing {} imports: [NgOptimizedImage], providers: [imageLoader], template: ` - -
    - -
    - `, + +
    + +
    + `, }) export class OversizedImageComponentFailing {} diff --git a/packages/core/test/bundling/image-directive/e2e/preconnect-check/preconnect-check.ts b/packages/core/test/bundling/image-directive/e2e/preconnect-check/preconnect-check.ts index 9e108a43368f..5f4a75a3d5f2 100644 --- a/packages/core/test/bundling/image-directive/e2e/preconnect-check/preconnect-check.ts +++ b/packages/core/test/bundling/image-directive/e2e/preconnect-check/preconnect-check.ts @@ -13,9 +13,9 @@ import {Component, Inject} from '../../../../../src/core'; selector: 'preconnect-check', imports: [NgOptimizedImage], template: ` - - - + + + `, providers: [ { diff --git a/packages/core/test/bundling/image-directive/index.html b/packages/core/test/bundling/image-directive/index.html index 1a625500e3fd..bdc1455eeed4 100644 --- a/packages/core/test/bundling/image-directive/index.html +++ b/packages/core/test/bundling/image-directive/index.html @@ -1,9 +1,9 @@ - + Image Directive Example - + diff --git a/packages/core/test/bundling/image-directive/playground.ts b/packages/core/test/bundling/image-directive/playground.ts index 610b5b94b8cb..bdeffcc76ebb 100644 --- a/packages/core/test/bundling/image-directive/playground.ts +++ b/packages/core/test/bundling/image-directive/playground.ts @@ -13,35 +13,35 @@ import {Component} from '../../../src/core'; selector: 'basic', styles: [ ` - h1 { - display: flex; - align-items: center; - } + h1 { + display: flex; + align-items: center; + } - main { - border: 1px solid blue; - margin: 16px; - padding: 16px; - } + main { + border: 1px solid blue; + margin: 16px; + padding: 16px; + } - .spacer { - height: 3000px; - } + .spacer { + height: 3000px; + } - main img { - width: 100%; - height: auto; - } - `, + main img { + width: 100%; + height: auto; + } + `, ], template: `

    - + Angular image app

    - +
    `, imports: [NgOptimizedImage], diff --git a/packages/core/test/bundling/package.json b/packages/core/test/bundling/package.json index 68a8fd020456..630598ba7d17 100644 --- a/packages/core/test/bundling/package.json +++ b/packages/core/test/bundling/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@angular/animations": "workspace:*", - "@angular/build": "21.1.0-next.0", + "@angular/build": "21.1.0-next.2", "@angular/common": "workspace:*", "@angular/compiler-cli": "workspace:*", "@angular/compiler": "workspace:*", diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 965f6e60df2f..a7a977bf2058 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -4,6 +4,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BASE_HREF", "APP_BOOTSTRAP_LISTENER", "APP_ID", @@ -63,6 +64,7 @@ "ComponentRef2", "Console", "ConsumerObserver", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -270,6 +272,7 @@ "SIGNAL", "SIGNAL_NODE", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "SafeValueImpl", @@ -385,7 +388,6 @@ "_stripOrigin", "_wasLastNodeCreated", "abortSignalToObservable", - "activateRoutes", "activeConsumer", "addAfterRenderSequencesForView", "addEmptyPathsToChildrenIfNeeded", @@ -415,6 +417,7 @@ "arrRemove", "arrayEquals", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -830,6 +833,7 @@ "isPublicRouterEvent", "isReadableStreamLike", "isRedirect", + "isRedirectingEvent", "isRedirectingNavigationCancelingError", "isRefreshingViews", "isRootView", @@ -1147,4 +1151,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/bundling/router/main.ts b/packages/core/test/bundling/router/main.ts index ba122355a250..791bf9b8a58b 100644 --- a/packages/core/test/bundling/router/main.ts +++ b/packages/core/test/bundling/router/main.ts @@ -23,11 +23,11 @@ import { @Component({ selector: 'app-list', template: ` - + `, imports: [RouterLink, RouterLinkActive], }) @@ -35,9 +35,8 @@ class ListComponent {} @Component({ selector: 'app-item', - template: ` - Item {{id}} -

    `, + template: ` Item {{ id }} +

    `, }) class ItemComponent implements OnInit { id = -1; diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index e9f7ff4db879..47f4af11c79a 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -4,6 +4,7 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", "ANIMATION_QUEUE", + "APPLICATION_IS_STABLE_TIMEOUT", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -42,6 +43,7 @@ "ComponentRef", "ComponentRef2", "ConsumerObserver", + "DEBUG_TASK_TRACKER", "DECLARATION_COMPONENT_VIEW", "DECLARATION_LCONTAINER", "DECLARATION_VIEW", @@ -165,6 +167,7 @@ "SCHEDULE_IN_ROOT_ZONE_DEFAULT", "SIGNAL", "SIMPLE_CHANGES_STORE", + "STABILITY_WARNING_THRESHOLD", "SVG_NAMESPACE", "SafeSubscriber", "Sanitizer", @@ -245,6 +248,7 @@ "areAnimationSupported", "arrRemove", "assertNotDestroyed", + "assertTypeDefined", "attachPatchData", "baseElement", "bind", @@ -629,4 +633,4 @@ ], "lazy": [] } -} \ No newline at end of file +} diff --git a/packages/core/test/component_fixture_spec.ts b/packages/core/test/component_fixture_spec.ts index bcb3de40f5ce..ec38ca626726 100644 --- a/packages/core/test/component_fixture_spec.ts +++ b/packages/core/test/component_fixture_spec.ts @@ -33,7 +33,7 @@ import {expect} from '@angular/private/testing/matchers'; @Component({ selector: 'simple-comp', - template: `Original {{simpleBinding}}`, + template: `Original {{ simpleBinding }}`, standalone: false, }) @Injectable() @@ -68,7 +68,7 @@ class MyIfComp { @Component({ selector: 'autodetect-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class AutoDetectComp { @@ -81,7 +81,7 @@ class AutoDetectComp { @Component({ selector: 'async-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class AsyncComp { @@ -112,7 +112,7 @@ class AsyncChildComp { @Component({ selector: 'async-change-comp', - template: ``, + template: ``, standalone: false, }) class AsyncChangeComp { @@ -125,7 +125,7 @@ class AsyncChangeComp { @Component({ selector: 'async-timeout-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class AsyncTimeoutComp { @@ -140,7 +140,7 @@ class AsyncTimeoutComp { @Component({ selector: 'nested-async-timeout-comp', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class NestedAsyncTimeoutComp { @@ -458,13 +458,13 @@ describe('ComponentFixture', () => { selector: 'defer-comp', imports: [DeferredComp, SecondDeferredComp], template: `
    - @defer (on immediate) { - - } - @defer (on idle) { - - } -
    `, + @defer (on immediate) { + + } + @defer (on idle) { + + } +
    `, }) class DeferComp {} diff --git a/packages/core/test/debug/debug_node_spec.ts b/packages/core/test/debug/debug_node_spec.ts index 2b049dc2e95a..4fd686a97e5d 100644 --- a/packages/core/test/debug/debug_node_spec.ts +++ b/packages/core/test/debug/debug_node_spec.ts @@ -75,9 +75,9 @@ class WithTitleDir { @Component({ selector: 'child-comp', template: `
    - Child -
    - `, + Child +
    + `, standalone: false, }) class ChildComp { @@ -92,10 +92,10 @@ class ChildComp { selector: 'parent-comp', viewProviders: [Logger], template: `
    - Parent -
    - - `, + Parent +
    + + `, standalone: false, }) class ParentComp { @@ -121,7 +121,7 @@ class CustomEmitter { @Component({ selector: 'events-comp', template: ` - `, + `, standalone: false, }) class EventsComp { @@ -156,9 +156,9 @@ class ConditionalContentComp { selector: 'conditional-parent-comp', viewProviders: [Logger], template: ` - - - `, + + + `, standalone: false, }) class ConditionalParentComp { @@ -172,9 +172,9 @@ class ConditionalParentComp { selector: 'using-for', viewProviders: [Logger], template: ` -
      -
    • -
    `, +
      +
    • +
    `, standalone: false, }) class UsingFor { @@ -193,19 +193,14 @@ class MyDir {} @Component({ selector: 'locals-comp', - template: ` -
    - `, + template: `
    `, standalone: false, }) class LocalsComp {} @Component({ selector: 'bank-account', - template: ` - Bank Name: {{bank}} - Account Id: {{id}} - `, + template: ` Bank Name: {{ bank }} Account Id: {{ id }} `, host: { 'class': 'static-class', '[class.absent-class]': 'false', @@ -221,9 +216,7 @@ class BankAccount { } @Component({ - template: ` -
    Some content
    - `, + template: `
    Some content
    `, standalone: false, }) class SimpleContentComp { @@ -233,13 +226,15 @@ class SimpleContentComp { @Component({ selector: 'test-app', template: ` - - `, + + `, standalone: false, }) class TestApp { @@ -285,11 +280,8 @@ class TestCmptWithViewContainerRef { @Component({ template: ` - -`, + + `, standalone: false, }) class TestCmptWithPropBindings { @@ -300,17 +292,19 @@ class TestCmptWithPropBindings { @Component({ template: ` - - - - - - - - - - -`, + + + + + + + + + + + `, standalone: false, }) class TestCmptWithPropInterpolation {} @@ -503,9 +497,7 @@ describe('debug element', () => { @Component({ selector: 'wrapper-component', - template: ` - - `, + template: ` `, standalone: false, }) class WrapperComponent {} @@ -538,9 +530,7 @@ describe('debug element', () => { @Component({ selector: 'proxy-component', - template: ` - - `, + template: ` `, standalone: false, }) class ProxyComponent {} @@ -548,11 +538,11 @@ describe('debug element', () => { @Component({ selector: 'wrapper-component', template: ` - - - - - `, + + + + + `, standalone: false, }) class WrapperComponent {} @@ -1229,27 +1219,27 @@ describe('debug element', () => { @Component({ selector: 'my-comp', template: ` -
    -

    - span.1 - span.2 -

    -

    - span.3 - span.4 -

    -
    -
    -

    - span.5 - span.6 -

    -

    - span.7 - span.8 -

    -
    - `, +
    +

    + span.1 + span.2 +

    +

    + span.3 + span.4 +

    +
    +
    +

    + span.5 + span.6 +

    +

    + span.7 + span.8 +

    +
    + `, standalone: false, }) class MyComp {} @@ -1390,7 +1380,8 @@ describe('debug element', () => { it('should match node name with declared casing', () => { @Component({ - template: `
    `, + template: `
    + `, standalone: false, }) class Wrapper {} diff --git a/packages/core/test/debug/stability_debug_spec.ts b/packages/core/test/debug/stability_debug_spec.ts new file mode 100644 index 000000000000..5d50d8859a38 --- /dev/null +++ b/packages/core/test/debug/stability_debug_spec.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {ApplicationInitStatus, PendingTasks, provideZoneChangeDetection} from '../../src/core'; +import {provideStabilityDebugging} from '../../src/application/stability_debug_impl'; +import {TestBed, fakeAsync, tick} from '@angular/core/testing'; + +describe('provideStabilityDebugging', () => { + let consoleWarnSpy: jasmine.Spy; + let consoleDebugSpy: jasmine.Spy; + let consoleInfoSpy: jasmine.Spy; + let consoleGroupEndSpy: jasmine.Spy; + + beforeEach(() => { + spyOn(global, 'Error').and.returnValue({stack: 'fake stack trace', name: 'Error', message: ''}); + consoleWarnSpy = spyOn(console, 'warn'); + consoleDebugSpy = spyOn(console, 'debug'); + consoleInfoSpy = spyOn(console, 'info'); + consoleGroupEndSpy = spyOn(console, 'groupEnd'); + TestBed.configureTestingModule({ + providers: [provideStabilityDebugging(), provideZoneChangeDetection()], + }); + }); + + function runInitializers() { + (TestBed.inject(ApplicationInitStatus) as any).runInitializers(); + } + + it('should log pending tasks if application does not stabilize', fakeAsync(() => { + const pendingTasks = TestBed.inject(PendingTasks); + + // Prevent stability + const removeTask = pendingTasks.add(); + + runInitializers(); + + tick(10_000); + + expect(consoleDebugSpy.calls.first().args[0]).toMatch(/Application did not stabilize/); + expect(consoleDebugSpy.calls.all()[1].args[0]).toMatch(/fake stack trace/); + + removeTask(); + })); + + it('should not log if application stabilizes within 9 seconds', fakeAsync(() => { + const pendingTasks = TestBed.inject(PendingTasks); + + // Prevent stability + const removeTask = pendingTasks.add(); + + runInitializers(); + + tick(5000); + removeTask(); // Stabilize + tick(4000); // Reach 9000 total + + expect(consoleDebugSpy).not.toHaveBeenCalled(); + })); +}); diff --git a/packages/core/test/defer_fixture_spec.ts b/packages/core/test/defer_fixture_spec.ts index 835e9a6d3e50..8c30f512928f 100644 --- a/packages/core/test/defer_fixture_spec.ts +++ b/packages/core/test/defer_fixture_spec.ts @@ -57,7 +57,7 @@ describe('DeferFixture', () => { }
    - `, + `, }) class DeferComp {} @@ -267,7 +267,8 @@ describe('DeferFixture', () => { } @loading { Loading... - }w + } + w
    `, }) diff --git a/packages/core/test/forward_ref_integration_spec.ts b/packages/core/test/forward_ref_integration_spec.ts index b026d4d8bbc5..11eca536bcd2 100644 --- a/packages/core/test/forward_ref_integration_spec.ts +++ b/packages/core/test/forward_ref_integration_spec.ts @@ -60,7 +60,8 @@ class App {} @Component({ selector: 'door', - template: `{{frame.name}}({{lock.name}})`, + template: `{{ frame.name }}({{ lock.name }})`, standalone: false, }) class Door { diff --git a/packages/core/test/legacy_animation/legacy_animation_integration_spec.ts b/packages/core/test/legacy_animation/legacy_animation_integration_spec.ts index 55f6ea1386a4..bef10faa9ceb 100644 --- a/packages/core/test/legacy_animation/legacy_animation_integration_spec.ts +++ b/packages/core/test/legacy_animation/legacy_animation_integration_spec.ts @@ -123,7 +123,11 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', template: ` -
    +
    `, animations: [ trigger('myAnimation', [ @@ -170,7 +174,11 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', template: ` -
    +
    `, animations: [trigger('myAnimation', [transition('* => go', [])])], standalone: false, @@ -198,7 +206,11 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', template: ` -
    +
    `, animations: [ trigger('myAnimation', [transition('* => go', [animate('1s', style({opacity: 0}))])]), @@ -237,9 +249,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should wait until the animations are finished until continuing', fakeAsync(() => { @Component({ selector: 'cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])]), ], @@ -275,9 +285,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should wait for a noop animation to finish before continuing', fakeAsync(() => { @Component({ selector: 'cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])]), ], @@ -312,9 +320,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should wait for active animations to finish even if they have already started', fakeAsync(() => { @Component({ selector: 'cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])]), ], @@ -351,9 +357,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should trigger a state change animation from void => state', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('void => *', [ @@ -403,9 +407,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [REUSABLE_ANIMATION], standalone: false, }) @@ -526,9 +528,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should allow a state value to be `0`', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('0 => 1', [ @@ -567,8 +567,12 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    - `, +
    + `, animations: [ trigger('myAnimation', [ transition('a => b', [ @@ -650,10 +654,14 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [trigger('myAnimation', [])], standalone: false, }) @@ -685,9 +693,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should only turn a view removal as into `void` state transition', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('void <=> *', [ @@ -827,9 +833,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should stringify boolean triggers to `1` and `0`', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('void => 1', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), @@ -882,9 +886,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should understand boolean values as `true` and `false` for transition animations', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('true => false', [ @@ -925,9 +927,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should understand boolean values as `true` and `false` for transition animations and apply the corresponding state() value', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state('true', style({color: 'red'})), @@ -1045,9 +1045,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should trigger a leave animation when the inner has ViewContainerRef injected', fakeAsync(() => { @Component({ selector: 'parent-cmp', - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1104,9 +1102,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should trigger a leave animation when the inner components host binding updates', fakeAsync(() => { @Component({ selector: 'parent-cmp', - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1226,9 +1222,7 @@ const DEFAULT_COMPONENT_ID = '1'; transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))]), ]), ], - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1286,9 +1280,7 @@ const DEFAULT_COMPONENT_ID = '1'; ]), ]), ], - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1360,9 +1352,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should not throw when the host element is removed and no animation triggers', fakeAsync(() => { @Component({ selector: 'parent-cmp', - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1404,9 +1394,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should properly evaluate pre/auto-style values when components are inserted/removed which contain host animations', fakeAsync(() => { @Component({ selector: 'parent-cmp', - template: ` - - `, + template: ` `, standalone: false, }) class ParentCmp { @@ -1447,9 +1435,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should cancel and merge in mid-animation styles into the follow-up animation, but only for animation keyframes that start right away', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('a => b', [ @@ -1504,9 +1490,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should provide the styling of previous players that are grouped', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('1 => 2', [ @@ -1568,10 +1552,10 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'ani-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('myAnimation', [ transition('1 => 2', [ @@ -1630,9 +1614,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should properly balance styles between states even if there are no destination state styles', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state('void', style({opacity: 0, width: '0px', height: '0px'})), @@ -1675,9 +1657,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should not apply the destination styles if the final animate step already contains styles', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state('void', style({color: 'red'})), @@ -1725,9 +1705,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should invoke an animation trigger that is state-less', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), @@ -1769,9 +1747,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should retain styles on the element once the animation is complete', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('green', [ state('*', style({backgroundColor: 'green'})), @@ -1913,11 +1889,14 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'ani-cmp', template: ` -
    - item{{ item }} -
    - `, +
    + item{{ item }} +
    + `, animations: [trigger('myAnimation', [])], standalone: false, }) @@ -1983,9 +1962,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]), trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])]), @@ -2051,9 +2028,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should properly cancel all existing animations when a removal occurs', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -2098,7 +2073,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'ani-cmp', template: ` -
    +
    `, @@ -2365,9 +2340,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should detect trigger changes based on object.value properties', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => 1', [animate(1234, style({opacity: 0}))]), @@ -2405,9 +2378,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should not render animations when the object expression value is the same as it was previously', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [transition('* => *', [animate(1234, style({opacity: 0}))])]), ], @@ -2444,9 +2415,7 @@ const DEFAULT_COMPONENT_ID = '1'; it("should update the final state styles when params update even if the expression hasn't changed", fakeAsync(() => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state('*', style({color: '{{ color }}'}), {params: {color: 'black'}}), @@ -2496,9 +2465,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should substitute in values if the provided state match is an object with values', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition( @@ -2544,9 +2511,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should retain substituted styles on the element once the animation is complete if referenced in the final state', fakeAsync(() => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state( @@ -2619,9 +2584,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should only evaluate final state param substitutions from the expression and state values and not from the transition options ', fakeAsync(() => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ state( @@ -2766,9 +2729,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'inner-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('inner', [ transition(':enter', [style({opacity: 0}), animate('1s', style({opacity: 1}))]), @@ -2805,9 +2766,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should detect when a value has incremented', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition(':increment', [animate(1234, style({background: 'red'}))]), @@ -2845,9 +2804,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should detect when a value has decremented', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition(':decrement', [animate(1234, style({background: 'red'}))]), @@ -2886,12 +2843,12 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    -
    - {{ item.value }} -
    -
    - `, +
    +
    + {{ item.value }} +
    +
    + `, animations: [ trigger('myAnimation', [ state('0', style({opacity: 0})), @@ -2969,8 +2926,8 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    - `, +
    + `, animations: [ trigger('myAnimation', [ transition('void => *', [ @@ -3010,8 +2967,12 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    - `, +
    + `, animations: [ trigger('myAnimation123', [ transition('* => b', [ @@ -3058,9 +3019,9 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    -
    - `, +
    +
    + `, animations: [ trigger('ani1', [ transition('* => a', [ @@ -3121,8 +3082,13 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'if-cmp', template: ` -
    - `, +
    + `, animations: [ trigger('ani1', [ transition('* => a', [ @@ -3182,9 +3148,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should handle a leave animation for multiple triggers even if not all triggers have their own leave transition specified', fakeAsync(() => { @Component({ selector: 'if-cmp', - template: ` -
    123
    - `, + template: `
    123
    `, animations: [ trigger('foo', [ transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), @@ -3269,8 +3233,12 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'my-cmp', template: ` -
    - `, +
    + `, animations: [trigger('myAnimation', [])], standalone: false, }) @@ -3305,8 +3273,13 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'my-cmp', template: ` -
    - `, +
    + `, animations: [trigger('myAnimation', [])], standalone: false, }) @@ -3345,16 +3318,20 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'my-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('parent', [ transition('* => go', [ @@ -3425,16 +3402,18 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'my-cmp', template: ` -
    -
    - {{ item }} -
    +
    +
    + {{ item }}
    - `, +
    + `, animations: [ trigger('parent', [ transition('* => go', [ @@ -3633,9 +3612,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should disable animations for the element that they are disabled on', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => 1, * => 2', [animate(1234, style({width: '100px'}))]), @@ -3773,7 +3750,11 @@ const DEFAULT_COMPONENT_ID = '1'; selector: 'if-cmp', template: `
    -
    +
    `, animations: [ @@ -3827,7 +3808,7 @@ const DEFAULT_COMPONENT_ID = '1';
    - `, + `, standalone: false, }) class ParentCmp { @@ -3837,9 +3818,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'child-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => go, * => goAgain', [ @@ -3887,7 +3866,7 @@ const DEFAULT_COMPONENT_ID = '1';
    - `, + `, standalone: false, }) class Cmp { @@ -3922,10 +3901,15 @@ const DEFAULT_COMPONENT_ID = '1'; template: `
    -
    +
    - `, + `, standalone: false, }) class Cmp { @@ -3968,9 +3952,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should convert hyphenated properties to camelcase by default', () => { @Component({ selector: 'cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -4015,9 +3997,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should convert hyphenated properties to camelCase by default that are auto/pre style properties', () => { @Component({ selector: 'cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -4058,9 +4038,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should throw neither state() or transition() are used inside of trigger()', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [trigger('myAnimation', [animate(1000, style({width: '100px'}))])], standalone: false, }) @@ -4086,10 +4064,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', - template: ` -
    -
    - `, + template: `
    `, animations: [trigger('anim', [transition(':enter', useAnimation(animationMetaData))])], standalone: false, }) @@ -4132,10 +4107,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', - template: ` -
    -
    - `, + template: `
    `, animations: [trigger('anim', [transition(':enter', useAnimation(animationMetaData))])], standalone: false, }) @@ -4178,10 +4150,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', - template: ` -
    -
    - `, + template: `
    `, animations: [ trigger('anim', [transition(':enter', useAnimation(animationMetaData, {delay: 1500}))]), ], @@ -4226,10 +4195,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', - template: ` -
    -
    - `, + template: `
    `, animations: [ trigger('anim', [ transition( @@ -4282,10 +4248,7 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', - template: ` -
    -
    - `, + template: `
    `, animations: [ trigger('anim', [ transition(':enter', useAnimation(animationMetaData, {delay: 34}), {delay: 200}), @@ -4328,9 +4291,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should combine multiple errors together into one exception when an animation fails to be built', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('foo', [ transition(':enter', []), @@ -4374,9 +4335,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should not throw an error if styles overlap in separate transitions', () => { @Component({ selector: 'if-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('void => *', [style({opacity: 0}), animate('0.5s 1s', style({opacity: 1}))]), @@ -4403,10 +4362,10 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('parent', [ transition( @@ -4578,7 +4537,7 @@ const DEFAULT_COMPONENT_ID = '1'; it('should throw when using an @prop listener, BrowserAnimationModule is imported, but there is no animation rule', () => { @Component({ - template: `
    `, + template: `
    `, standalone: false, }) class Cmp {} @@ -4597,10 +4556,10 @@ const DEFAULT_COMPONENT_ID = '1'; @Component({ selector: 'cmp', template: ` -
    -

    -
    - `, +
    +

    +
    + `, animations: [trigger('myAnimation', [transition('void => *', triggerAnimationData)])], standalone: false, }) diff --git a/packages/core/test/legacy_animation/legacy_animation_query_integration_spec.ts b/packages/core/test/legacy_animation/legacy_animation_query_integration_spec.ts index 61223e7f16de..9ad83102ede4 100644 --- a/packages/core/test/legacy_animation/legacy_animation_query_integration_spec.ts +++ b/packages/core/test/legacy_animation/legacy_animation_query_integration_spec.ts @@ -271,8 +271,7 @@ import {HostListener} from '../../src/metadata/directives'; selector: 'ani-cmp', template: `
    -
    -
    +
    `, animations: [ @@ -527,9 +526,7 @@ import {HostListener} from '../../src/metadata/directives'; it('should retain style values when :self is used inside of a query', () => { @Component({ selector: 'ani-cmp', - template: ` -
    - `, + template: `
    `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -640,12 +637,12 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'ani-cmp', template: ` -
    -
    - {{ item }} +
    +
    + {{ item }} +
    -
    - `, + `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -712,12 +709,12 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'ani-cmp', template: ` -
    -
    - {{ item }} +
    +
    + {{ item }} +
    -
    - `, + `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -1751,12 +1748,12 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'cmp', template: ` -
    -
    - {{ item }} +
    +
    + {{ item }} +
    -
    - `, + `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -1792,12 +1789,12 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'cmp', template: ` -
    -
    - {{ item }} +
    +
    + {{ item }} +
    -
    - `, + `, animations: [ trigger('myAnimation', [ transition('* => go', [ @@ -2253,8 +2250,7 @@ import {HostListener} from '../../src/metadata/directives'; template: `
    -
    -
    +
    `, @@ -2396,10 +2392,10 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'child-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('myChildAnimation', [ transition(':leave', [style({opacity: 0}), animate('1s', style({opacity: 1}))]), @@ -2462,9 +2458,7 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'child-cmp', - template: ` - - `, + template: ` `, standalone: false, }) class ChildCmp {} @@ -2472,10 +2466,10 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'nested-child-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('myChildAnimation', [ transition(':leave', [style({opacity: 0}), animate('1s', style({opacity: 1}))]), @@ -2524,7 +2518,8 @@ import {HostListener} from '../../src/metadata/directives'; selector: 'ani-cmp', template: `
    - this
    child
    + this +
    child
    `, animations: [ @@ -2577,15 +2572,11 @@ import {HostListener} from '../../src/metadata/directives';
    -
    - text -
    +
    text
    -
    - text2 -
    +
    text2
    `, @@ -2754,10 +2745,32 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    -
    -
    -
    +
    +
    +
    +
    `, standalone: false, @@ -2860,11 +2873,13 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    - +
    +
    `, standalone: false, @@ -2892,9 +2907,7 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    +
    `, standalone: false, }) @@ -2975,11 +2988,19 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    -
    -
    +
    +
    +
    `, standalone: false, }) @@ -3208,16 +3229,16 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    -
    -
    -
    -
    -
    -
    -
    -
    - `, +
    +
    +
    +
    +
    +
    +
    +
    +
    + `, standalone: false, }) class Cmp { @@ -3271,16 +3292,16 @@ import {HostListener} from '../../src/metadata/directives'; ]), ], template: ` -
    -
    -
    -
    -
    -
    -
    -
    -
    - `, +
    +
    +
    +
    +
    +
    +
    +
    +
    + `, standalone: false, }) class Cmp { @@ -3321,7 +3342,7 @@ import {HostListener} from '../../src/metadata/directives'; template: `
    -
    +
    `, @@ -3366,10 +3387,10 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('parent', [ state('true', style({backgroundColor: 'red'})), @@ -3426,10 +3447,10 @@ import {HostListener} from '../../src/metadata/directives'; @Component({ selector: 'cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('parent', [ state('true', style({backgroundColor: 'red'})), diff --git a/packages/core/test/legacy_animation/legacy_animations_with_web_animations_integration_spec.ts b/packages/core/test/legacy_animation/legacy_animations_with_web_animations_integration_spec.ts index 56e79930daa4..a7c1b8e0abba 100644 --- a/packages/core/test/legacy_animation/legacy_animations_with_web_animations_integration_spec.ts +++ b/packages/core/test/legacy_animation/legacy_animations_with_web_animations_integration_spec.ts @@ -169,12 +169,12 @@ import {isNode} from '@angular/private/testing'; @Component({ selector: 'ani-cmp', template: ` -
    -
    - - {{ item }} -
    +
    +
    + - {{ item }}
    - `, +
    + `, animations: [ trigger('myAnimation', [ transition('* => *', [style({height: '!'}), animate(1000, style({height: '*'}))]), @@ -245,21 +245,19 @@ import {isNode} from '@angular/private/testing'; ` .box { width: 500px; - overflow:hidden; - background:orange; - line-height:300px; - font-size:100px; - text-align:center; + overflow: hidden; + background: orange; + line-height: 300px; + font-size: 100px; + text-align: center; } `, ], template: ` - -
    -
    - ... -
    - `, + +
    +
    ...
    + `, animations: [ trigger('slide', [ state('void', style({height: '0px'})), @@ -322,28 +320,28 @@ import {isNode} from '@angular/private/testing'; selector: 'my-app', styles: [ ` - .list .outer { - overflow:hidden; - } - .list .inner { - box-sizing: border-box; - height: 50px; - } - `, + .list .outer { + overflow: hidden; + } + .list .inner { + box-sizing: border-box; + height: 50px; + } + `, ], template: ` - - - -
    -
    -
    -
    - {{ item }} -
    -
    + + + +
    +
    +
    +
    + {{ item }}
    - `, +
    +
    + `, animations: [ trigger('list', [ transition(':enter', []), @@ -434,9 +432,7 @@ import {isNode} from '@angular/private/testing'; it('should compute intermediate styles properly when an animation is cancelled', () => { @Component({ selector: 'ani-cmp', - template: ` -
    ...
    - `, + template: `
    ...
    `, animations: [ trigger('myAnimation', [ transition('* => a', [ diff --git a/packages/core/test/linker/change_detection_integration_spec.ts b/packages/core/test/linker/change_detection_integration_spec.ts index e7b91af888f2..ee3823eda87c 100644 --- a/packages/core/test/linker/change_detection_integration_spec.ts +++ b/packages/core/test/linker/change_detection_integration_spec.ts @@ -1390,7 +1390,10 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ @Component({ selector: 'main-cmp', - template: ``, + template: ``, standalone: false, }) class MainComp { @@ -1402,7 +1405,10 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ @Component({ selector: 'outer-cmp', - template: ``, + template: ``, standalone: false, }) class OuterComp { @@ -1416,7 +1422,10 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ @Component({ selector: 'inner-cmp', - template: `>`, + template: `>`, standalone: false, }) class InnerComp { @@ -1507,7 +1516,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ describe('class binding', () => { it('should coordinate class attribute and class host binding', () => { @Component({ - template: `
    `, + template: `
    `, standalone: false, }) class Comp { @@ -1635,7 +1644,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ @Component({ selector: 'my-component', - template: ``, + template: ``, standalone: false, }) class MyComponent { diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index ba17202712a8..6efc1f70eb1a 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -2286,7 +2286,7 @@ describe('integration tests', function () { @Component({ selector: 'cmp-with-default-interpolation', - template: `{{text}}`, + template: `{{ text }}`, standalone: false, }) class ComponentWithDefaultInterpolation { @@ -3097,7 +3097,8 @@ class DirectiveThrowingAnError { @Component({ selector: 'component-with-template', - template: `No View Decorator:
    {{item}}
    `, + template: `No View Decorator: +
    {{ item }}
    `, standalone: false, }) class ComponentWithTemplate { diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index bbd5a8e80354..37d68710097c 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -103,7 +103,7 @@ class SomePipe { @Component({ selector: 'comp', - template: `
    `, + template: `
    `, standalone: false, }) class CompUsingModuleDirectiveAndPipe {} diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index 9324b88e5e92..60c0104fe90e 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -272,7 +272,9 @@ describe('projection', () => { it('should redistribute non-continuous blocks of nodes when the shadow dom changes', () => { @Component({ selector: 'child', - template: `()`, + template: `()`, standalone: false, }) class Child { @@ -612,7 +614,9 @@ describe('projection', () => { it('should project nodes into nested templates and the main template', () => { @Component({ selector: 'content-in-main-and-template', - template: `()`, + template: `()`, standalone: false, }) class ContentInMainAndTemplateComponent {} @@ -1039,7 +1043,7 @@ class Tree { @Component({ selector: 'cmp-d', - template: `{{tagName}}`, + template: `{{ tagName }}`, standalone: false, }) class CmpD { @@ -1051,7 +1055,7 @@ class CmpD { @Component({ selector: 'cmp-c', - template: `{{tagName}}`, + template: `{{ tagName }}`, standalone: false, }) class CmpC { @@ -1077,42 +1081,42 @@ class CmpA {} @Component({ selector: 'cmp-b11', - template: `{{'b11'}}`, + template: `{{ 'b11' }}`, standalone: false, }) class CmpB11 {} @Component({ selector: 'cmp-b12', - template: `{{'b12'}}`, + template: `{{ 'b12' }}`, standalone: false, }) class CmpB12 {} @Component({ selector: 'cmp-b21', - template: `{{'b21'}}`, + template: `{{ 'b21' }}`, standalone: false, }) class CmpB21 {} @Component({ selector: 'cmp-b22', - template: `{{'b22'}}`, + template: `{{ 'b22' }}`, standalone: false, }) class CmpB22 {} @Component({ selector: 'cmp-a1', - template: `{{'a1'}}`, + template: `{{ 'a1' }}`, standalone: false, }) class CmpA1 {} @Component({ selector: 'cmp-a2', - template: `{{'a2'}}`, + template: `{{ 'a2' }}`, standalone: false, }) class CmpA2 {} diff --git a/packages/core/test/render3/component_ref_spec.ts b/packages/core/test/render3/component_ref_spec.ts index 66d5ba8f4384..db2dd2764065 100644 --- a/packages/core/test/render3/component_ref_spec.ts +++ b/packages/core/test/render3/component_ref_spec.ts @@ -316,7 +316,7 @@ describe('ComponentFactory', () => { it('should allow setting inputs on the ComponentRef', () => { const inputChangesLog: string[] = []; - @Component({template: `{{input}}`, standalone: false}) + @Component({template: `{{ input }}`, standalone: false}) class DynamicCmp implements OnChanges { ngOnChanges(changes: SimpleChanges): void { const inChange = changes['input']; @@ -346,7 +346,7 @@ describe('ComponentFactory', () => { }); it('should allow setting mapped inputs on the ComponentRef', () => { - @Component({template: `{{input}}`, standalone: false}) + @Component({template: `{{ input }}`, standalone: false}) class DynamicCmp { @Input('publicName') input: string | undefined; } @@ -383,7 +383,7 @@ describe('ComponentFactory', () => { it('should mark components for check when setting an input on a ComponentRef', () => { @Component({ - template: `{{input}}`, + template: `{{ input }}`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) @@ -403,7 +403,7 @@ describe('ComponentFactory', () => { it('should not set input if value is the same as the previous', () => { let log: string[] = []; - @Component({template: `{{input}}`}) + @Component({template: `{{ input }}`}) class DynamicCmp { @Input() set input(v: string) { @@ -423,7 +423,7 @@ describe('ComponentFactory', () => { it('marks parents dirty so component is not "shielded" by a non-dirty OnPush parent', () => { @Component({ - template: `{{input}}`, + template: `{{ input }}`, selector: 'dynamic', }) class DynamicCmp { diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index f040e4d57a5d..0d11e7095c3f 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -135,9 +135,7 @@ describe('element discovery', () => { it('should cache the element context on a element was preemptively monkey-patched', () => { @Component({ selector: 'structured-comp', - template: ` -
    - `, + template: `
    `, }) class StructuredComp {} @@ -160,10 +158,10 @@ describe('element discovery', () => { @Component({ selector: 'structured-comp', template: ` -
    -

    -
    - `, +
    +

    +
    + `, }) class StructuredComp {} @@ -184,9 +182,7 @@ describe('element discovery', () => { it('should be able to pull in element context data even if the element is decorated using styling', () => { @Component({ selector: 'structured-comp', - template: ` -
    - `, + template: `
    `, }) class StructuredComp {} @@ -225,13 +221,13 @@ describe('element discovery', () => { @Component({ selector: 'projector-comp', template: ` - welcome -
    -

    - -

    -
    - `, + welcome +
    +

    + +

    +
    + `, }) class ProjectorComp {} @@ -239,13 +235,13 @@ describe('element discovery', () => { selector: 'parent-comp', imports: [ProjectorComp], template: ` -
    - -

    this content is projected

    - this content is projected also -
    -
    - `, +
    + +

    this content is projected

    + this content is projected also +
    +
    + `, }) class ParentComp {} @@ -300,9 +296,7 @@ describe('element discovery', () => { it('should return `null` when an element context is retrieved that is a DOM node that was not created by Angular', () => { @Component({ selector: 'structured-comp', - template: ` -
    - `, + template: `
    `, }) class StructuredComp {} @@ -382,9 +376,9 @@ describe('element discovery', () => { selector: 'structured-comp', imports: [MyDir1, MyDir2, MyDir3], template: ` -
    -
    - `, +
    +
    + `, }) class StructuredComp {} @@ -455,9 +449,7 @@ describe('element discovery', () => { @Component({ selector: 'child-comp', - template: ` -
    - `, + template: `
    `, }) class ChildComp { constructor() { @@ -468,9 +460,7 @@ describe('element discovery', () => { @Component({ selector: 'parent-comp', imports: [ChildComp, MyDir1, MyDir2], - template: ` - - `, + template: ` `, }) class ParentComp {} @@ -522,10 +512,10 @@ describe('element discovery', () => { @Component({ selector: 'child-comp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class ChildComp {} @@ -533,10 +523,10 @@ describe('element discovery', () => { selector: 'parent-comp', imports: [ChildComp], template: ` -
    - -
    - `, +
    + +
    + `, }) class ParentComp {} @@ -567,9 +557,7 @@ describe('sanitization', () => { it('should sanitize data using the provided sanitization interface', () => { @Component({ selector: 'sanitize-this', - template: ` - - `, + template: ` `, }) class SanitizationComp { href = ''; @@ -612,7 +600,7 @@ describe('sanitization', () => { selector: '[unsafeUrlHostBindingDir]', }) class UnsafeUrlHostBindingDir { - @HostBinding() cite: any = 'http://cite-dir-value'; + @HostBinding() href: any = 'http://href-dir-value'; constructor() { hostBindingDir = this; @@ -622,9 +610,7 @@ describe('sanitization', () => { @Component({ selector: 'sanitize-this', imports: [UnsafeUrlHostBindingDir], - template: ` -
    - `, + template: ` text `, }) class SimpleComp {} @@ -640,16 +626,16 @@ describe('sanitization', () => { ], }); const fixture = TestBed.createComponent(SimpleComp); - hostBindingDir!.cite = 'http://foo'; + hostBindingDir!.href = 'http://foo'; fixture.detectChanges(); - const anchor = fixture.nativeElement.querySelector('blockquote')!; - expect(anchor.getAttribute('cite')).toEqual('http://bar'); + const anchor = fixture.nativeElement.querySelector('a')!; + expect(anchor.getAttribute('href')).toEqual('http://bar'); - hostBindingDir!.cite = sanitizer.bypassSecurityTrustUrl('http://foo'); + hostBindingDir!.href = sanitizer.bypassSecurityTrustUrl('http://foo'); fixture.detectChanges(); - expect(anchor.getAttribute('cite')).toEqual('http://foo'); + expect(anchor.getAttribute('href')).toEqual('http://foo'); }); }); diff --git a/packages/core/test/render3/jit/declare_component_spec.ts b/packages/core/test/render3/jit/declare_component_spec.ts index 292238efb4ca..282729650669 100644 --- a/packages/core/test/render3/jit/declare_component_spec.ts +++ b/packages/core/test/render3/jit/declare_component_spec.ts @@ -175,13 +175,11 @@ describe('component declaration jit compilation', () => { contentQueries: functionContaining([ // "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for query flag // without a read token, and bind to the full query result. - /contentQuery[^(]*\(dirIndex,_c0,4\)/, - '(ctx.byRef = _t)', - // "byToken" should use `staticContentQuery` with `3` // (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as // read token, and bind to the first result in the query result. - /contentQuery[^(]*\(dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + /contentQuery[^(]*\(dirIndex,_c0,4\)[^(]*\(dirIndex,[^,]*String[^,]*,\s*3,[^)]*ElementRef[^)]*\)/, + '(ctx.byRef = _t)', '(ctx.byToken = _t.first)', ]), }); @@ -213,13 +211,11 @@ describe('component declaration jit compilation', () => { viewQuery: functionContaining([ // "byRef" should use `viewQuery` with `0` (`QueryFlags.none`) for query flag without a read // token, and bind to the full query result. - /viewQuery[^(]*\(_c0,4\)/, - '(ctx.byRef = _t)', - // "byToken" should use `viewQuery` with `3` // (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as // read token, and bind to the first result in the query result. - /viewQuery[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + /viewQuery[^(]*\(_c0,4\)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + '(ctx.byRef = _t)', '(ctx.byToken = _t.first)', ]), }); diff --git a/packages/core/test/render3/jit/declare_directive_spec.ts b/packages/core/test/render3/jit/declare_directive_spec.ts index 74109d9c7002..8f36fdc4db2b 100644 --- a/packages/core/test/render3/jit/declare_directive_spec.ts +++ b/packages/core/test/render3/jit/declare_directive_spec.ts @@ -118,13 +118,11 @@ describe('directive declaration jit compilation', () => { contentQueries: functionContaining([ // "byRef" should use `contentQuery` with `0` (`QueryFlags.descendants|QueryFlags.isStatic`) // for query flag without a read token, and bind to the full query result. - /contentQuery[^(]*\(dirIndex,_c0,4\)/, - '(ctx.byRef = _t)', - // "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`) // for query flag and `ElementRef` as read token, and bind to the first result in the // query result. - /contentQuery[^(]*\([^,]*dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + /contentQuery[^(]*\(dirIndex,_c0,4\)\(dirIndex,[^,]*String[^,]*,\s*3,[^)]*ElementRef[^)]*\)/, + '(ctx.byRef = _t)', '(ctx.byToken = _t.first)', ]), }); @@ -177,13 +175,11 @@ describe('directive declaration jit compilation', () => { viewQuery: functionContaining([ // "byRef" should use `viewQuery` with`0` (`QueryFlags.none`) for query flag without a read // token, and bind to the full query result. - /viewQuery[^(]*\(_c0,4\)/, - '(ctx.byRef = _t)', - // "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`) // for query flag and `ElementRef` as read token, and bind to the first result in the // query result. - /viewQuery[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + /viewQuery[^(]*\(_c0,4\)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, + '(ctx.byRef = _t)', '(ctx.byToken = _t.first)', ]), }); diff --git a/packages/core/test/render3/providers_spec.ts b/packages/core/test/render3/providers_spec.ts index cc429441c00d..b2bc893dbe86 100644 --- a/packages/core/test/render3/providers_spec.ts +++ b/packages/core/test/render3/providers_spec.ts @@ -1041,7 +1041,7 @@ describe('providers', () => { let hostComponent: HostComponent | null = null; @Component({ - template: `{{s}}`, + template: `{{ s }}`, selector: 'embedded-cmp', }) class EmbeddedComponent { diff --git a/packages/core/test/render3/reactivity_spec.ts b/packages/core/test/render3/reactivity_spec.ts index 3de8867ec9bc..e28bf4e4afa2 100644 --- a/packages/core/test/render3/reactivity_spec.ts +++ b/packages/core/test/render3/reactivity_spec.ts @@ -91,6 +91,7 @@ describe('reactivity', () => { expect(isStable).toEqual([true, false]); appRef.tick(); + await appRef.whenStable(); expect(isStable).toEqual([true, false, true]); }); @@ -437,7 +438,7 @@ describe('reactivity', () => { @Component({ imports: [Dir], - template: `{{data}}`, + template: `{{ data }}`, changeDetection: ChangeDetectionStrategy.OnPush, }) class TestCmp {} @@ -634,7 +635,7 @@ describe('reactivity', () => { imports: [WithInputSetter], template: ` | - `, + `, }) class Cmp {} @@ -830,10 +831,10 @@ describe('reactivity', () => { selector: 'driver-cmp', imports: [TestCmp], template: ` - @if (cond) { - - } - `, + @if (cond) { + + } + `, }) class DriverCmp { cond = false; diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index c31e109142ea..1aba5b49800a 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -151,8 +151,11 @@ export class HostBindingDir { selector: 'component-with-prop-bindings', template: `
    -

    -

    +

    `, standalone: false, }) @@ -163,9 +166,7 @@ export class ComponentWithPropBindings { @Component({ selector: 'simple-app', - template: ` - - - `, + template: ` - `, standalone: false, }) export class SimpleApp {} @@ -2672,14 +2673,26 @@ describe('TestBed module teardown', () => { it('should remove the styles associated with a test component when the test module is torn down', () => { @Component({ template: 'Hello', - styles: [`span {color: hotpink;}`], + styles: [ + ` + span { + color: hotpink; + } + `, + ], standalone: false, }) class StyledComp1 {} @Component({ template: '
    Hello
    ', - styles: [`div {color: red;}`], + styles: [ + ` + div { + color: red; + } + `, + ], standalone: false, }) class StyledComp2 {} diff --git a/packages/core/test/zone/ng_zone_spec.ts b/packages/core/test/zone/ng_zone_spec.ts index acaba114ba9b..4b12f8adb595 100644 --- a/packages/core/test/zone/ng_zone_spec.ts +++ b/packages/core/test/zone/ng_zone_spec.ts @@ -927,7 +927,7 @@ function commonTests() { @Component({ template: `
    - {{clicked ? 'clicked' : '' }} + {{ clicked ? 'clicked' : '' }} `, }) class OuterComponent {} diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 582fa52dda30..14e67c8de619 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -880,6 +880,8 @@ export class TestBedImpl implements TestBed { // The behavior should be that TestBed.tick, ComponentFixture.detectChanges, and ApplicationRef.tick all result in the test fixtures // getting synchronized, regardless of whether they are autoDetect: true. // Automatic scheduling (zone or zoneless) will call _tick which will _not_ include fixtures with autoDetect: false + // If this does get changed, we will need a new flag for the scheduler to use to omit the microtask scheduling + // from a tick initiated by tests. (appRef as any).includeAllTestViews = true; appRef.tick(); } finally { diff --git a/packages/core/testing/src/test_bed_compiler.ts b/packages/core/testing/src/test_bed_compiler.ts index dc25dfa2a5a7..d5620e71ced6 100644 --- a/packages/core/testing/src/test_bed_compiler.ts +++ b/packages/core/testing/src/test_bed_compiler.ts @@ -1070,15 +1070,30 @@ export class TestBedCompiler { private patchDefWithProviderOverrides(declaration: Type, field: string): void { const def = (declaration as any)[field]; - if (def && def.providersResolver) { + + if (!def) { + return; + } + + if (def.viewProvidersResolver) { this.maybeStoreNgDef(field, declaration); + const viewProvidersResolver = def.viewProvidersResolver; + this.storeFieldOfDefOnType(declaration, field, 'viewProvidersResolver'); + def.viewProvidersResolver = (ngDef: DirectiveDef) => + viewProvidersResolver(ngDef, this.processProviderOverrides); + } - const resolver = def.providersResolver; - const processProvidersFn = (providers: Provider[]) => this.getOverriddenProviders(providers); + if (def.providersResolver) { + this.maybeStoreNgDef(field, declaration); + const providersResolver = def.providersResolver; this.storeFieldOfDefOnType(declaration, field, 'providersResolver'); - def.providersResolver = (ngDef: DirectiveDef) => resolver(ngDef, processProvidersFn); + def.providersResolver = (ngDef: DirectiveDef) => + providersResolver(ngDef, this.processProviderOverrides); } } + + private processProviderOverrides = (providers: Provider[]) => + this.getOverriddenProviders(providers); } function initResolvers(): Resolvers { diff --git a/packages/examples/common/ngComponentOutlet/ts/module.ts b/packages/examples/common/ngComponentOutlet/ts/module.ts index da93861830ce..93bdbd45aa91 100644 --- a/packages/examples/common/ngComponentOutlet/ts/module.ts +++ b/packages/examples/common/ngComponentOutlet/ts/module.ts @@ -44,7 +44,8 @@ export class Greeter { @Component({ selector: 'complete-component', - template: `{{ label() }}: {{ greeter.suffix }}`, + template: `{{ label() }}: {{ greeter.suffix }}`, }) export class CompleteComponent { label = input.required(); diff --git a/packages/examples/common/pipes/ts/keyvalue_pipe.ts b/packages/examples/common/pipes/ts/keyvalue_pipe.ts index 13369bae7eb7..dd097aec557c 100644 --- a/packages/examples/common/pipes/ts/keyvalue_pipe.ts +++ b/packages/examples/common/pipes/ts/keyvalue_pipe.ts @@ -13,21 +13,20 @@ import {Component} from '@angular/core'; @Component({ selector: 'keyvalue-pipe', imports: [KeyValuePipe], - template: ` - -

    Object

    - @for (item of object | keyvalue; track item.key) { -
    {{ item.key }}:{{ item.value }}
    - } -

    Map

    - @for (item of map | keyvalue; track item.key) { -
    {{ item.key }}:{{ item.value }}
    - } -

    Natural order

    - @for (item of map | keyvalue : null; track item.key) { -
    {{ item.key }}:{{ item.value }}
    - } -
    `, + template: ` +

    Object

    + @for (item of object | keyvalue; track item.key) { +
    {{ item.key }}:{{ item.value }}
    + } +

    Map

    + @for (item of map | keyvalue; track item.key) { +
    {{ item.key }}:{{ item.value }}
    + } +

    Natural order

    + @for (item of map | keyvalue: null; track item.key) { +
    {{ item.key }}:{{ item.value }}
    + } +
    `, }) export class KeyValuePipeComponent { object: {[key: number]: string} = {2: 'foo', 1: 'bar'}; diff --git a/packages/examples/common/pipes/ts/slice_pipe.ts b/packages/examples/common/pipes/ts/slice_pipe.ts index cdca6805bc7e..9ab465e7185a 100644 --- a/packages/examples/common/pipes/ts/slice_pipe.ts +++ b/packages/examples/common/pipes/ts/slice_pipe.ts @@ -32,7 +32,7 @@ export class SlicePipeStringComponent { selector: 'slice-list-pipe', imports: [SlicePipe], template: `
      - @for(i of collection | slice: 1 : 3; track $index) { + @for (i of collection | slice: 1 : 3; track $index) {
    • {{ i }}
    • }
    `, diff --git a/packages/examples/core/di/ts/contentChild/content_child_example.ts b/packages/examples/core/di/ts/contentChild/content_child_example.ts index b6cee364adf0..88647ddb29dd 100644 --- a/packages/examples/core/di/ts/contentChild/content_child_example.ts +++ b/packages/examples/core/di/ts/contentChild/content_child_example.ts @@ -29,10 +29,10 @@ export class Tab { imports: [Tab, Pane], template: ` - @if(shouldShow()) { - + @if (shouldShow()) { + } @else { - + } diff --git a/packages/examples/core/di/ts/contentChildren/content_children_example.ts b/packages/examples/core/di/ts/contentChildren/content_children_example.ts index cc4573cc57f2..791a58704cc7 100644 --- a/packages/examples/core/di/ts/contentChildren/content_children_example.ts +++ b/packages/examples/core/di/ts/contentChildren/content_children_example.ts @@ -42,7 +42,7 @@ export class Tab { - @if(shouldShow()) { + @if (shouldShow()) { diff --git a/packages/examples/core/di/ts/viewChild/view_child_example.ts b/packages/examples/core/di/ts/viewChild/view_child_example.ts index 03a7f2fb0fe7..952165c23ce9 100644 --- a/packages/examples/core/di/ts/viewChild/view_child_example.ts +++ b/packages/examples/core/di/ts/viewChild/view_child_example.ts @@ -20,10 +20,10 @@ export class Pane { selector: 'example-app', imports: [Pane], template: ` - @if(shouldShow()) { - + @if (shouldShow()) { + } @else { - + } diff --git a/packages/examples/core/di/ts/viewChildren/view_children_example.ts b/packages/examples/core/di/ts/viewChildren/view_children_example.ts index daae6d79914f..2195d21d5002 100644 --- a/packages/examples/core/di/ts/viewChildren/view_children_example.ts +++ b/packages/examples/core/di/ts/viewChildren/view_children_example.ts @@ -28,10 +28,10 @@ export class Pane { selector: 'example-app', imports: [Pane], template: ` - - - @if(shouldShow()) { - + + + @if (shouldShow()) { + } diff --git a/packages/examples/core/ts/change_detect/change-detection.ts b/packages/examples/core/ts/change_detect/change-detection.ts index ec53a93984f1..31c63cb142c7 100644 --- a/packages/examples/core/ts/change_detect/change-detection.ts +++ b/packages/examples/core/ts/change_detect/change-detection.ts @@ -44,9 +44,8 @@ class DataListProvider { @Component({ selector: 'giant-list', - template: ` -
      - @for( d of dataProvider.data; track $index) { + template: `
        + @for (d of dataProvider.data; track $index) {
      • Item {{ d }}
      • }
      `, @@ -67,7 +66,7 @@ class GiantList { selector: 'app', providers: [DataListProvider], imports: [GiantList], - template: ``, + template: ``, }) class App {} // #enddocregion detach diff --git a/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts b/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts index d62af19430d4..01c45782606c 100644 --- a/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts +++ b/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts @@ -16,7 +16,7 @@ import {FormArray, FormControl, FormGroup} from '@angular/forms'; template: `
      - @for(city of cities.controls; track city;) { + @for (city of cities.controls; track city) { }
      diff --git a/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts b/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts index 33bc5371d256..17db630e3b0a 100644 --- a/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts +++ b/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts @@ -15,7 +15,7 @@ import {FormControl, FormGroup, Validators} from '@angular/forms'; selector: 'example-app', template: ` - @if(name.invalid) { + @if (name.invalid) {

      Name is invalid.

      }
      diff --git a/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts b/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts index 92465bb266e7..66b0c1ac9a94 100644 --- a/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts +++ b/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts @@ -15,7 +15,7 @@ import {NgForm} from '@angular/forms'; selector: 'example-app', template: ` - @if(nameCtrl.invalid) { + @if (nameCtrl.invalid) {

      Name is invalid.

      }
      diff --git a/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts b/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts index 4402d3d0671c..f0cccb07aaf7 100644 --- a/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts +++ b/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts @@ -15,7 +15,7 @@ import {FormControl, FormGroup, Validators} from '@angular/forms'; selector: 'example-app', template: ` - @if(first.invalid) { + @if (first.invalid) {
      Name is too short.
      } diff --git a/packages/examples/index.html b/packages/examples/index.html index 73e2e8fea333..678a9b1b7f30 100644 --- a/packages/examples/index.html +++ b/packages/examples/index.html @@ -1,4 +1,4 @@ - + diff --git a/packages/examples/upgrade/index.html b/packages/examples/upgrade/index.html index 89baa29946c7..c89a6feaa270 100644 --- a/packages/examples/upgrade/index.html +++ b/packages/examples/upgrade/index.html @@ -1,4 +1,4 @@ - + diff --git a/packages/examples/upgrade/static/ts/full/module.ts b/packages/examples/upgrade/static/ts/full/module.ts index 676bf028f26b..343c75ca867e 100644 --- a/packages/examples/upgrade/static/ts/full/module.ts +++ b/packages/examples/upgrade/static/ts/full/module.ts @@ -51,7 +51,7 @@ export class TextFormatter { Super Hero -
      +
      `, standalone: false, }) diff --git a/packages/examples/upgrade/static/ts/lite/module.ts b/packages/examples/upgrade/static/ts/lite/module.ts index 830b46b9318c..f6951c54b02d 100644 --- a/packages/examples/upgrade/static/ts/lite/module.ts +++ b/packages/examples/upgrade/static/ts/lite/module.ts @@ -70,10 +70,10 @@ class HeroesService {
      - - Super Hero - -
      + + Super Hero + +
    `, diff --git a/packages/forms/package.json b/packages/forms/package.json index d0a30b799c57..e5922b27b53a 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -8,13 +8,13 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "dependencies": { - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "@standard-schema/spec": "^1.0.0" }, "peerDependencies": { "@angular/core": "0.0.0-PLACEHOLDER", "@angular/common": "0.0.0-PLACEHOLDER", "@angular/platform-browser": "0.0.0-PLACEHOLDER", - "@standard-schema/spec": "^1.0.0", "rxjs": "^6.5.3 || ^7.4.0" }, "repository": { diff --git a/packages/forms/signals/compat/src/api/compat_form.ts b/packages/forms/signals/compat/src/api/compat_form.ts index 55d404546ca9..d213fda1d423 100644 --- a/packages/forms/signals/compat/src/api/compat_form.ts +++ b/packages/forms/signals/compat/src/api/compat_form.ts @@ -27,7 +27,7 @@ export type CompatFormOptions = Omit; * compatibility with Reactive forms by accepting Reactive controls as a part of the data. * * @example - * ``` + * ```ts * const lastName = new FormControl('lastName'); * * const nameModel = signal({ @@ -40,7 +40,8 @@ export type CompatFormOptions = Omit; * }); * * nameForm.last().value(); // lastName, not FormControl - * + * ``` + * * @param model A writable signal that contains the model data for the form. The resulting field * structure will match the shape of the model and any changes to the form data will be written to * the model. @@ -57,7 +58,7 @@ export function compatForm(model: WritableSignal): FieldTree( * compatibility with Reactive forms by accepting Reactive controls as a part of the data. * * @example - * ``` + * ```ts * const lastName = new FormControl('lastName'); * * const nameModel = signal({ diff --git a/packages/forms/signals/compat/src/api/compat_validation_error.ts b/packages/forms/signals/compat/src/api/compat_validation_error.ts index 135e981766c5..ea4b903fae62 100644 --- a/packages/forms/signals/compat/src/api/compat_validation_error.ts +++ b/packages/forms/signals/compat/src/api/compat_validation_error.ts @@ -7,8 +7,8 @@ */ import {AbstractControl} from '@angular/forms'; +import {ValidationError} from '../../../src/api/rules/validation/validation_errors'; import {FieldTree} from '../../../src/api/types'; -import {ValidationError} from '../../../src/api/validation_errors'; /** * An error used for compat errors. @@ -19,7 +19,7 @@ import {ValidationError} from '../../../src/api/validation_errors'; export class CompatValidationError implements ValidationError { readonly kind: string = 'compat'; readonly control: AbstractControl; - readonly field!: FieldTree; + readonly fieldTree!: FieldTree; readonly context: T; readonly message?: string; diff --git a/packages/forms/signals/compat/src/api/di.ts b/packages/forms/signals/compat/src/api/di.ts index 635eae2c532a..07e1d211328f 100644 --- a/packages/forms/signals/compat/src/api/di.ts +++ b/packages/forms/signals/compat/src/api/di.ts @@ -15,11 +15,11 @@ import type {SignalFormsConfig} from '../../../src/api/di'; * @experimental 21.0.1 */ export const NG_STATUS_CLASSES: SignalFormsConfig['classes'] = { - 'ng-touched': (state) => state.touched(), - 'ng-untouched': (state) => !state.touched(), - 'ng-dirty': (state) => state.dirty(), - 'ng-pristine': (state) => !state.dirty(), - 'ng-valid': (state) => state.valid(), - 'ng-invalid': (state) => state.invalid(), - 'ng-pending': (state) => state.pending(), + 'ng-touched': ({state}) => state().touched(), + 'ng-untouched': ({state}) => !state().touched(), + 'ng-dirty': ({state}) => state().dirty(), + 'ng-pristine': ({state}) => !state().dirty(), + 'ng-valid': ({state}) => state().valid(), + 'ng-invalid': ({state}) => state().invalid(), + 'ng-pending': ({state}) => state().pending(), }; diff --git a/packages/forms/signals/compat/src/compat_structure.ts b/packages/forms/signals/compat/src/compat_structure.ts index 097b1f03955d..258d6e563970 100644 --- a/packages/forms/signals/compat/src/compat_structure.ts +++ b/packages/forms/signals/compat/src/compat_structure.ts @@ -6,7 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed, Signal, signal, WritableSignal} from '@angular/core'; +import { + computed, + Signal, + signal, + WritableSignal, + ɵRuntimeError as RuntimeError, +} from '@angular/core'; +import {SignalFormsErrorCode} from '../../src/errors'; import {FormFieldManager} from '../../src/field/manager'; import {FieldNode, ParentFieldNode} from '../../src/field/node'; import { @@ -101,22 +108,30 @@ function getControlValueSignal(options: CompatFieldNodeOptions) { */ export class CompatStructure extends FieldNodeStructure { override value: WritableSignal; - override keyInParent: Signal = (() => { - throw new Error('Compat nodes do not use keyInParent.'); - }) as unknown as Signal; + override keyInParent: Signal; override root: FieldNode; override pathKeys: Signal; override readonly children = signal([]); - override readonly childrenMap = signal(undefined); + override readonly childrenMap = computed(() => undefined); override readonly parent: ParentFieldNode | undefined; override readonly fieldManager: FormFieldManager; constructor(node: FieldNode, options: CompatFieldNodeOptions) { - super(options.logic); + super(options.logic, node, () => { + throw new RuntimeError( + SignalFormsErrorCode.COMPAT_NO_CHILDREN, + ngDevMode && `Compat nodes don't have children.`, + ); + }); this.value = getControlValueSignal(options); this.parent = getParentFromOptions(options); this.root = this.parent?.structure.root ?? node; this.fieldManager = getFieldManagerFromOptions(options); + + const identityInParent = options.kind === 'child' ? options.identityInParent : undefined; + const initialKeyInParent = options.kind === 'child' ? options.initialKeyInParent : undefined; + this.keyInParent = this.createKeyInParent(options, identityInParent, initialKeyInParent); + this.pathKeys = computed(() => this.parent ? [...this.parent.structure.pathKeys(), this.keyInParent()] : [], ); diff --git a/packages/forms/signals/compat/src/compat_validation_state.ts b/packages/forms/signals/compat/src/compat_validation_state.ts index 8052ed389912..e17982cd44c2 100644 --- a/packages/forms/signals/compat/src/compat_validation_state.ts +++ b/packages/forms/signals/compat/src/compat_validation_state.ts @@ -8,7 +8,7 @@ import {computed, Signal} from '@angular/core'; import {AbstractControl} from '@angular/forms'; -import {ValidationError} from '../../src/api/validation_errors'; +import {ValidationError} from '../../src/api/rules/validation/validation_errors'; import {calculateValidationSelfStatus, ValidationState} from '../../src/field/validation'; import type {CompatValidationError} from './api/compat_validation_error'; import {getControlStatusSignal} from './compat_field_node'; diff --git a/packages/forms/signals/public_api.ts b/packages/forms/signals/public_api.ts index 9e3e9110aa47..02509a843047 100644 --- a/packages/forms/signals/public_api.ts +++ b/packages/forms/signals/public_api.ts @@ -11,14 +11,12 @@ * @description * Entry point for all public APIs of this package. */ -export * from './src/api/async'; export * from './src/api/control'; -export * from './src/api/debounce'; export * from './src/api/di'; export * from './src/api/field_directive'; -export * from './src/api/logic'; -export * from './src/api/metadata'; +export * from './src/api/rules'; +export * from './src/api/rules/debounce'; +export * from './src/api/rules/metadata'; +export * from './src/api/rules/validation/validation_errors'; export * from './src/api/structure'; export * from './src/api/types'; -export * from './src/api/validation_errors'; -export * from './src/api/validators'; diff --git a/packages/forms/signals/src/api/control.ts b/packages/forms/signals/src/api/control.ts index a05dfe5d7267..4533810d7b49 100644 --- a/packages/forms/signals/src/api/control.ts +++ b/packages/forms/signals/src/api/control.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ -import {InputSignal, ModelSignal, OutputRef} from '@angular/core'; +import {InputSignal, InputSignalWithTransform, ModelSignal, OutputRef} from '@angular/core'; +import {ValidationError, type WithOptionalField} from './rules/validation/validation_errors'; import type {DisabledReason} from './types'; -import {ValidationError, type WithOptionalField} from './validation_errors'; /** * The base set of properties shared by all form control contracts. @@ -25,82 +25,100 @@ export interface FormUiControl { * An input to receive the errors for the field. If implemented, the `Field` directive will * automatically bind errors from the bound field to this input. */ - readonly errors?: InputSignal[]>; + readonly errors?: + | InputSignal[]> + | InputSignalWithTransform[], unknown>; /** * An input to receive the disabled status for the field. If implemented, the `Field` directive * will automatically bind the disabled status from the bound field to this input. */ - readonly disabled?: InputSignal; + readonly disabled?: InputSignal | InputSignalWithTransform; /** * An input to receive the reasons for the disablement of the field. If implemented, the `Field` * directive will automatically bind the disabled reason from the bound field to this input. */ - readonly disabledReasons?: InputSignal[]>; + readonly disabledReasons?: + | InputSignal[]> + | InputSignalWithTransform[], unknown>; /** * An input to receive the readonly status for the field. If implemented, the `Field` directive * will automatically bind the readonly status from the bound field to this input. */ - readonly readonly?: InputSignal; + readonly readonly?: InputSignal | InputSignalWithTransform; /** * An input to receive the hidden status for the field. If implemented, the `Field` directive * will automatically bind the hidden status from the bound field to this input. */ - readonly hidden?: InputSignal; + readonly hidden?: InputSignal | InputSignalWithTransform; /** * An input to receive the invalid status for the field. If implemented, the `Field` directive * will automatically bind the invalid status from the bound field to this input. */ - readonly invalid?: InputSignal; + readonly invalid?: InputSignal | InputSignalWithTransform; /** * An input to receive the pending status for the field. If implemented, the `Field` directive * will automatically bind the pending status from the bound field to this input. */ - readonly pending?: InputSignal; + readonly pending?: InputSignal | InputSignalWithTransform; /** * An input to receive the touched status for the field. If implemented, the `Field` directive * will automatically bind the touched status from the bound field to this input. */ - readonly touched?: ModelSignal | InputSignal | OutputRef; + readonly touched?: + | ModelSignal + | InputSignal + | InputSignalWithTransform + | OutputRef; /** * An input to receive the dirty status for the field. If implemented, the `Field` directive * will automatically bind the dirty status from the bound field to this input. */ - readonly dirty?: InputSignal; + readonly dirty?: InputSignal | InputSignalWithTransform; /** * An input to receive the name for the field. If implemented, the `Field` directive will * automatically bind the name from the bound field to this input. */ - readonly name?: InputSignal; + readonly name?: InputSignal | InputSignalWithTransform; /** * An input to receive the required status for the field. If implemented, the `Field` directive * will automatically bind the required status from the bound field to this input. */ - readonly required?: InputSignal; + readonly required?: InputSignal | InputSignalWithTransform; /** * An input to receive the min value for the field. If implemented, the `Field` directive will * automatically bind the min value from the bound field to this input. */ - readonly min?: InputSignal; + readonly min?: + | InputSignal + | InputSignalWithTransform; /** * An input to receive the min length for the field. If implemented, the `Field` directive will * automatically bind the min length from the bound field to this input. */ - readonly minLength?: InputSignal; + readonly minLength?: + | InputSignal + | InputSignalWithTransform; /** * An input to receive the max value for the field. If implemented, the `Field` directive will * automatically bind the max value from the bound field to this input. */ - readonly max?: InputSignal; + readonly max?: + | InputSignal + | InputSignalWithTransform; /** * An input to receive the max length for the field. If implemented, the `Field` directive will * automatically bind the max length from the bound field to this input. */ - readonly maxLength?: InputSignal; + readonly maxLength?: + | InputSignal + | InputSignalWithTransform; /** * An input to receive the value patterns for the field. If implemented, the `Field` directive * will automatically bind the value patterns from the bound field to this input. */ - readonly pattern?: InputSignal; + readonly pattern?: + | InputSignal + | InputSignalWithTransform; } /** diff --git a/packages/forms/signals/src/api/di.ts b/packages/forms/signals/src/api/di.ts index 44b024ca6527..86e100fae688 100644 --- a/packages/forms/signals/src/api/di.ts +++ b/packages/forms/signals/src/api/di.ts @@ -8,7 +8,7 @@ import {type Provider} from '@angular/core'; import {SIGNAL_FORMS_CONFIG} from '../field/di'; -import type {FieldState} from './types'; +import type {Field} from './field_directive'; /** * Configuration options for signal forms. @@ -17,7 +17,7 @@ import type {FieldState} from './types'; */ export interface SignalFormsConfig { /** A map of CSS class names to predicate functions that determine when to apply them. */ - classes?: {[className: string]: (state: FieldState) => boolean}; + classes?: {[className: string]: (state: Field) => boolean}; } /** diff --git a/packages/forms/signals/src/api/field_directive.ts b/packages/forms/signals/src/api/field_directive.ts index 2870d6163646..cb605eb76ac4 100644 --- a/packages/forms/signals/src/api/field_directive.ts +++ b/packages/forms/signals/src/api/field_directive.ts @@ -8,15 +8,18 @@ import { computed, + ɵɵcontrolCreate as createControlBinding, Directive, effect, + ElementRef, inject, InjectionToken, Injector, input, + ɵcontrolUpdate as updateControlBinding, ɵCONTROL, - ɵControl, ɵInteropControl, + type ɵControl, } from '@angular/core'; import {NG_VALUE_ACCESSOR, NgControl} from '@angular/forms'; import {InteropNgControl} from '../controls/interop_ng_control'; @@ -31,9 +34,17 @@ import type {FieldTree} from './types'; * @experimental 21.0.0 */ export const FIELD = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'FIELD' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'FIELD' : '', ); +/** + * Instructions for dynamically binding a {@link Field} to a form control. + */ +const controlInstructions = { + create: createControlBinding, + update: updateControlBinding, +} as const; + /** * Binds a form `FieldTree` to a UI control that edits it. A UI control can be one of several things: * 1. A native HTML input or textarea @@ -60,15 +71,23 @@ export const FIELD = new InjectionToken>( {provide: NgControl, useFactory: () => inject(Field).getOrCreateNgControl()}, ], }) -export class Field implements ɵControl { - private readonly injector = inject(Injector); +// This directive should `implements ɵControl`, but actually adding that breaks people's +// builds because part of the public API is marked `@internal` and stripped. +// Instead we have an type check below that enforces this in a non-breaking way. +export class Field { + readonly element = inject>(ElementRef).nativeElement; + readonly injector = inject(Injector); + readonly field = input.required>(); + readonly state = computed(() => this.field()()); + + readonly [ɵCONTROL] = controlInstructions; + private config = inject(SIGNAL_FORMS_CONFIG, {optional: true}); + /** @internal */ readonly classes = Object.entries(this.config?.classes ?? {}).map( - ([className, computation]) => [className, computed(() => computation(this.state()))] as const, + ([className, computation]) => + [className, computed(() => computation(this as Field))] as const, ); - readonly field = input.required>(); - readonly state = computed(() => this.field()()); - readonly [ɵCONTROL] = undefined; /** Any `ControlValueAccessor` instances provided on the host element. */ private readonly controlValueAccessors = inject(NG_VALUE_ACCESSOR, {optional: true, self: true}); @@ -76,7 +95,11 @@ export class Field implements ɵControl { /** A lazily instantiated fake `NgControl`. */ private interopNgControl: InteropNgControl | undefined; - /** A `ControlValueAccessor`, if configured, for the host component. */ + /** + * A `ControlValueAccessor`, if configured, for the host component. + * + * @internal + */ get ɵinteropControl(): ɵInteropControl | undefined { return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined; } @@ -86,7 +109,7 @@ export class Field implements ɵControl { return (this.interopNgControl ??= new InteropNgControl(this.state)); } - // TODO: https://github.com/orgs/angular/projects/60/views/1?pane=issue&itemId=131861631 + /** @internal */ ɵregister() { // Register this control on the field it is currently bound to. We do this at the end of // initialization so that it only runs if we are actually syncing with this control @@ -108,3 +131,8 @@ export class Field implements ɵControl { ); } } + +// We can't add `implements ɵControl` to `Field` even though it should conform to the interface. +// Instead we enforce it here through some utility types. +type Check = T; +type FieldImplementsɵControl = Check extends ɵControl ? true : false>; diff --git a/packages/forms/signals/src/api/logic.ts b/packages/forms/signals/src/api/logic.ts deleted file mode 100644 index ece46cff099c..000000000000 --- a/packages/forms/signals/src/api/logic.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {addDefaultField} from '../field/validation'; -import {FieldPathNode} from '../schema/path_node'; -import {assertPathIsCurrent} from '../schema/schema'; -import {AggregateMetadataKey, createMetadataKey, MetadataKey} from './metadata'; -import type { - FieldContext, - SchemaPath, - FieldValidator, - LogicFn, - PathKind, - TreeValidator, - SchemaPathRules, -} from './types'; -import {ensureCustomValidationResult} from './validators/util'; - -/** - * Adds logic to a field to conditionally disable it. A disabled field does not contribute to the - * validation, touched/dirty, or other state of its parent field. - * - * @param path The target path to add the disabled logic to. - * @param logic A reactive function that returns `true` (or a string reason) when the field is disabled, - * and `false` when it is not disabled. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function disabled( - path: SchemaPath, - logic?: string | NoInfer>, -): void { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addDisabledReasonRule((ctx) => { - let result: boolean | string = true; - if (typeof logic === 'string') { - result = logic; - } else if (logic) { - result = logic(ctx as FieldContext); - } - if (typeof result === 'string') { - return {field: ctx.field, message: result}; - } - return result ? {field: ctx.field} : undefined; - }); -} - -/** - * Adds logic to a field to conditionally make it readonly. A readonly field does not contribute to - * the validation, touched/dirty, or other state of its parent field. - * - * @param path The target path to make readonly. - * @param logic A reactive function that returns `true` when the field is readonly. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function readonly( - path: SchemaPath, - logic: NoInfer> = () => true, -) { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addReadonlyRule(logic); -} - -/** - * Adds logic to a field to conditionally hide it. A hidden field does not contribute to the - * validation, touched/dirty, or other state of its parent field. - * - * If a field may be hidden it is recommended to guard it with an `@if` in the template: - * ``` - * @if (!email().hidden()) { - * - * - * } - * ``` - * - * @param path The target path to add the hidden logic to. - * @param logic A reactive function that returns `true` when the field is hidden. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function hidden( - path: SchemaPath, - logic: NoInfer>, -): void { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addHiddenRule(logic); -} - -/** - * Adds logic to a field to determine if the field has validation errors. - * - * @param path The target path to add the validation logic to. - * @param logic A `Validator` that returns the current validation errors. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function validate( - path: SchemaPath, - logic: NoInfer>, -): void { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addSyncErrorRule((ctx) => { - return ensureCustomValidationResult( - addDefaultField(logic(ctx as FieldContext), ctx.field), - ); - }); -} - -/** - * Adds logic to a field to determine if the field or any of its child fields has validation errors. - * - * @param path The target path to add the validation logic to. - * @param logic A `TreeValidator` that returns the current validation errors. - * Errors returned by the validator may specify a target field to indicate an error on a child field. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function validateTree( - path: SchemaPath, - logic: NoInfer>, -): void { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addSyncTreeErrorRule((ctx) => - addDefaultField(logic(ctx as FieldContext), ctx.field), - ); -} - -/** - * Adds a value to an {@link AggregateMetadataKey} of a field. - * - * @param path The target path to set the aggregate metadata on. - * @param key The aggregate metadata key - * @param logic A function that receives the `FieldContext` and returns a value to add to the aggregate metadata. - * @template TValue The type of value stored in the field the logic is bound to. - * @template TMetadataItem The type of value the metadata aggregates over. - * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) - * - * @category logic - * @experimental 21.0.0 - */ -export function aggregateMetadata< - TValue, - TMetadataItem, - TPathKind extends PathKind = PathKind.Root, ->( - path: SchemaPath, - key: AggregateMetadataKey, - logic: NoInfer>, -): void { - assertPathIsCurrent(path); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addAggregateMetadataRule(key, logic); -} - -/** - * Creates a new {@link MetadataKey} and defines the value of the new metadata key for the given field. - * - * @param path The path to define the metadata for. - * @param factory A factory function that creates the value for the metadata. - * This function is **not** reactive. It is run once when the field is created. - * @returns The newly created metadata key - * - * @category logic - * @experimental 21.0.0 - */ -export function metadata( - path: SchemaPath, - factory: (ctx: FieldContext) => TData, -): MetadataKey; - -/** - * Defines the value of a {@link MetadataKey} for a given field. - * - * @param path The path to define the metadata for. - * @param key The metadata key to define. - * @param factory A factory function that creates the value for the metadata. - * This function is **not** reactive. It is run once when the field is created. - * @returns The given metadata key - * - * @category logic - * @experimental 21.0.0 - */ -export function metadata( - path: SchemaPath, - key: MetadataKey, - factory: (ctx: FieldContext) => TData, -): MetadataKey; - -export function metadata( - path: SchemaPath, - ...rest: - | [(ctx: FieldContext) => TData] - | [MetadataKey, (ctx: FieldContext) => TData] -): MetadataKey { - assertPathIsCurrent(path); - - let key: MetadataKey; - let factory: (ctx: FieldContext) => TData; - if (rest.length === 2) { - [key, factory] = rest; - } else { - [factory] = rest; - } - key ??= createMetadataKey(); - - const pathNode = FieldPathNode.unwrapFieldPath(path); - pathNode.builder.addMetadataFactory(key, factory as (ctx: FieldContext) => unknown); - return key; -} diff --git a/packages/forms/signals/src/api/metadata.ts b/packages/forms/signals/src/api/metadata.ts deleted file mode 100644 index 6958c29fa6a2..000000000000 --- a/packages/forms/signals/src/api/metadata.ts +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/** - * Represents metadata that may be defined on a field when it is created using a `metadata` rule - * in the schema. A particular `MetadataKey` can only be defined on a particular field **once**. - * - * @category logic - * @experimental 21.0.0 - */ -export class MetadataKey { - private brand!: TValue; - - /** Use {@link createMetadataKey}. */ - private constructor() {} -} - -/** - * Creates a {@link MetadataKey}. - * - * @experimental 21.0.0 - */ -export function createMetadataKey(): MetadataKey { - return new (MetadataKey as new () => MetadataKey)(); -} - -/** - * Represents metadata that is aggregated from multiple parts according to the key's reducer - * function. A value can be contributed to the aggregated value for a field using an - * `aggregateMetadata` rule in the schema. There may be multiple rules in a schema that contribute - * values to the same `AggregateMetadataKey` of the same field. - * - * @experimental 21.0.0 - */ -export class AggregateMetadataKey { - private brand!: [TAcc, TItem]; - - /** Use {@link reducedMetadataKey}. */ - private constructor( - readonly reduce: (acc: TAcc, item: TItem) => TAcc, - readonly getInitial: () => TAcc, - ) {} -} - -/** - * Creates an {@link AggregateMetadataKey} that reduces its individual values into an accumulated - * value using the given `reduce` and `getInitial` functions. - * @param reduce The reducer function. - * @param getInitial A function that gets the initial value for the reduce operation. - * - * @experimental 21.0.0 - */ -export function reducedMetadataKey( - reduce: (acc: TAcc, item: TItem) => TAcc, - getInitial: NoInfer<() => TAcc>, -): AggregateMetadataKey { - return new (AggregateMetadataKey as new ( - reduce: (acc: TAcc, item: TItem) => TAcc, - getInitial: () => TAcc, - ) => AggregateMetadataKey)(reduce, getInitial); -} - -/** - * Creates an {@link AggregateMetadataKey} that reduces its individual values into a list. - * - * @experimental 21.0.0 - */ -export function listMetadataKey(): AggregateMetadataKey { - return reducedMetadataKey( - (acc, item) => (item === undefined ? acc : [...acc, item]), - () => [], - ); -} - -/** - * Creates {@link AggregateMetadataKey} that reduces its individual values by taking their min. - * - * @experimental 21.0.0 - */ -export function minMetadataKey(): AggregateMetadataKey { - return reducedMetadataKey( - (prev, next) => { - if (prev === undefined) { - return next; - } - if (next === undefined) { - return prev; - } - return Math.min(prev, next); - }, - () => undefined, - ); -} - -/** - * Creates {@link AggregateMetadataKey} that reduces its individual values by taking their max. - * - * @experimental 21.0.0 - */ -export function maxMetadataKey(): AggregateMetadataKey { - return reducedMetadataKey( - (prev, next) => { - if (prev === undefined) { - return next; - } - if (next === undefined) { - return prev; - } - return Math.max(prev, next); - }, - () => undefined, - ); -} - -/** - * Creates an {@link AggregateMetadataKey} that reduces its individual values by logically or-ing - * them. - * - * @experimental 21.0.0 - */ -export function orMetadataKey(): AggregateMetadataKey { - return reducedMetadataKey( - (prev, next) => prev || next, - () => false, - ); -} - -/** - * Creates an {@link AggregateMetadataKey} that reduces its individual values by logically and-ing - * them. - * - * @experimental 21.0.0 - */ -export function andMetadataKey(): AggregateMetadataKey { - return reducedMetadataKey( - (prev, next) => prev && next, - () => true, - ); -} - -/** - * An {@link AggregateMetadataKey} representing whether the field is required. - * - * @category validation - * @experimental 21.0.0 - */ -export const REQUIRED: AggregateMetadataKey = orMetadataKey(); - -/** - * An {@link AggregateMetadataKey} representing the min value of the field. - * - * @category validation - * @experimental 21.0.0 - */ -export const MIN: AggregateMetadataKey = maxMetadataKey(); - -/** - * An {@link AggregateMetadataKey} representing the max value of the field. - * - * @category validation - * @experimental 21.0.0 - */ -export const MAX: AggregateMetadataKey = minMetadataKey(); - -/** - * An {@link AggregateMetadataKey} representing the min length of the field. - * - * @category validation - * @experimental 21.0.0 - */ -export const MIN_LENGTH: AggregateMetadataKey = - maxMetadataKey(); - -/** - * An {@link AggregateMetadataKey} representing the max length of the field. - * - * @category validation - * @experimental 21.0.0 - */ -export const MAX_LENGTH: AggregateMetadataKey = - minMetadataKey(); - -/** - * An {@link AggregateMetadataKey} representing the patterns the field must match. - * - * @category validation - * @experimental 21.0.0 - */ -export const PATTERN: AggregateMetadataKey = listMetadataKey(); diff --git a/packages/forms/signals/src/api/debounce.ts b/packages/forms/signals/src/api/rules/debounce.ts similarity index 86% rename from packages/forms/signals/src/api/debounce.ts rename to packages/forms/signals/src/api/rules/debounce.ts index 3a893151ab67..1503e6483d5c 100644 --- a/packages/forms/signals/src/api/debounce.ts +++ b/packages/forms/signals/src/api/rules/debounce.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DEBOUNCER} from '../field/debounce'; -import {FieldPathNode} from '../schema/path_node'; -import {assertPathIsCurrent} from '../schema/schema'; -import type {Debouncer, PathKind, SchemaPath, SchemaPathRules} from './types'; +import {DEBOUNCER} from '../../field/debounce'; +import {FieldPathNode} from '../../schema/path_node'; +import {assertPathIsCurrent} from '../../schema/schema'; +import type {Debouncer, PathKind, SchemaPath, SchemaPathRules} from '../types'; /** * Configures the frequency at which a form field is updated by UI events. @@ -36,7 +36,7 @@ export function debounce( : durationOrDebouncer > 0 ? debounceForDuration(durationOrDebouncer) : immediate; - pathNode.builder.addAggregateMetadataRule(DEBOUNCER, () => debouncer); + pathNode.builder.addMetadataRule(DEBOUNCER, () => debouncer); } function debounceForDuration(durationInMilliseconds: number): Debouncer { diff --git a/packages/forms/signals/src/api/rules/disabled.ts b/packages/forms/signals/src/api/rules/disabled.ts new file mode 100644 index 000000000000..7cff058cef32 --- /dev/null +++ b/packages/forms/signals/src/api/rules/disabled.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {FieldPathNode} from '../../schema/path_node'; +import {assertPathIsCurrent} from '../../schema/schema'; +import type {FieldContext, LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; + +/** + * Adds logic to a field to conditionally disable it. A disabled field does not contribute to the + * validation, touched/dirty, or other state of its parent field. + * + * @param path The target path to add the disabled logic to. + * @param logic A reactive function that returns `true` (or a string reason) when the field is disabled, + * and `false` when it is not disabled. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function disabled( + path: SchemaPath, + logic?: string | NoInfer>, +): void { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addDisabledReasonRule((ctx) => { + let result: boolean | string = true; + if (typeof logic === 'string') { + result = logic; + } else if (logic) { + result = logic(ctx as FieldContext); + } + if (typeof result === 'string') { + return {fieldTree: ctx.fieldTree, message: result}; + } + return result ? {fieldTree: ctx.fieldTree} : undefined; + }); +} diff --git a/packages/forms/signals/src/api/rules/hidden.ts b/packages/forms/signals/src/api/rules/hidden.ts new file mode 100644 index 000000000000..e0f89dff234f --- /dev/null +++ b/packages/forms/signals/src/api/rules/hidden.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {FieldPathNode} from '../../schema/path_node'; +import {assertPathIsCurrent} from '../../schema/schema'; +import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; + +/** + * Adds logic to a field to conditionally hide it. A hidden field does not contribute to the + * validation, touched/dirty, or other state of its parent field. + * + * If a field may be hidden it is recommended to guard it with an `@if` in the template: + * ``` + * @if (!email().hidden()) { + * + * + * } + * ``` + * + * @param path The target path to add the hidden logic to. + * @param logic A reactive function that returns `true` when the field is hidden. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function hidden( + path: SchemaPath, + logic: NoInfer>, +): void { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addHiddenRule(logic); +} diff --git a/packages/forms/signals/src/api/rules/index.ts b/packages/forms/signals/src/api/rules/index.ts new file mode 100644 index 000000000000..8a911003bc2c --- /dev/null +++ b/packages/forms/signals/src/api/rules/index.ts @@ -0,0 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +export * from './disabled'; +export * from './hidden'; +export * from './metadata'; +export * from './readonly'; +export * from './validation'; diff --git a/packages/forms/signals/src/api/rules/metadata.ts b/packages/forms/signals/src/api/rules/metadata.ts new file mode 100644 index 000000000000..368cd18685f2 --- /dev/null +++ b/packages/forms/signals/src/api/rules/metadata.ts @@ -0,0 +1,302 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {type Signal} from '@angular/core'; +import {FieldPathNode} from '../../schema/path_node'; +import {assertPathIsCurrent} from '../../schema/schema'; +import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; + +/** + * Sets a value for the {@link MetadataKey} for this field. + * + * This value is combined via a reduce operation defined by the particular key, + * since multiple rules in the schema might set values for it. + * + * @param path The target path to set the metadata for. + * @param key The metadata key + * @param logic A function that receives the `FieldContext` and returns a value for the metadata. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TKey The type of metadata key. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function metadata< + TValue, + TKey extends MetadataKey, + TPathKind extends PathKind = PathKind.Root, +>( + path: SchemaPath, + key: TKey, + logic: NoInfer, TPathKind>>, +): TKey { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addMetadataRule(key, logic); + return key; +} + +/** + * A reducer that determines the accumulated value for a metadata key by reducing the individual + * values contributed from `metadata()` rules. + * + * @template TAcc The accumulated type of the reduce operation. + * @template TItem The type of the individual items that are reduced over. + * @experimental 21.0.2 + */ +export interface MetadataReducer { + /** The reduce function. */ + reduce: (acc: TAcc, item: TItem) => TAcc; + /** Gets the initial accumulated value. */ + getInitial: () => TAcc; +} +export const MetadataReducer = { + /** Creates a reducer that accumulates a list of its individual item values. */ + list(): MetadataReducer { + return { + reduce: (acc, item) => (item === undefined ? acc : [...acc, item]), + getInitial: () => [], + }; + }, + + /** Creates a reducer that accumulates the min of its individual item values. */ + min(): MetadataReducer { + return { + reduce: (acc, item) => { + if (acc === undefined || item === undefined) { + return acc ?? item; + } + return Math.min(acc, item); + }, + getInitial: () => undefined, + }; + }, + + /** Creates a reducer that accumulates a the max of its individual item values. */ + max(): MetadataReducer { + return { + reduce: (prev, next) => { + if (prev === undefined || next === undefined) { + return prev ?? next; + } + return Math.max(prev, next); + }, + getInitial: () => undefined, + }; + }, + + /** Creates a reducer that logically or's its accumulated value with each individual item value. */ + or(): MetadataReducer { + return { + reduce: (prev, next) => prev || next, + getInitial: () => false, + }; + }, + + /** Creates a reducer that logically and's its accumulated value with each individual item value. */ + and(): MetadataReducer { + return { + reduce: (prev, next) => prev && next, + getInitial: () => true, + }; + }, + + /** Creates a reducer that always takes the next individual item value as the accumulated value. */ + override, +} as const; + +function override(): MetadataReducer; +function override(getInitial: () => T): MetadataReducer; +function override(getInitial?: () => T): MetadataReducer { + return { + reduce: (_, item) => item, + getInitial: () => getInitial?.(), + }; +} + +/** + * Represents metadata that is aggregated from multiple parts according to the key's reducer + * function. A value can be contributed to the aggregated value for a field using an + * `metadata` rule in the schema. There may be multiple rules in a schema that contribute + * values to the same `MetadataKey` of the same field. + * + * @template TRead The type read from the `FieldState` for this key + * @template TWrite The type written to this key using the `metadata()` rule + * @template TAcc The type of the reducer's accumulated value. + * + * @experimental 21.0.0 + */ +export class MetadataKey { + private brand!: [TRead, TWrite, TAcc]; + + /** Use {@link reducedMetadataKey}. */ + protected constructor( + readonly reducer: MetadataReducer, + readonly create: ((s: Signal) => TRead) | undefined, + ) {} +} + +/** + * Extracts the the type that can be set into the given metadata key type using the `metadata()` rule. + * + * @template TKey The `MetadataKey` type + * + * @experimental 21.0.0 + */ +export type MetadataSetterType = + TKey extends MetadataKey ? TWrite : never; + +/** + * Creates a metadata key used to contain a computed value. + * The last value set on a given field tree node overrides any previously set values. + * + * @template TWrite The type written to this key using the `metadata()` rule + * + * @experimental 21.0.0 + */ +export function createMetadataKey(): MetadataKey< + Signal, + TWrite, + TWrite | undefined +>; +/** + * Creates a metadata key used to contain a computed value. + * + * @param reducer The reducer used to combine individually set values into the final computed value. + * @template TWrite The type written to this key using the `metadata()` rule + * @template TAcc The type of the reducer's accumulated value. + * + * @experimental 21.0.0 + */ +export function createMetadataKey( + reducer: MetadataReducer, +): MetadataKey, TWrite, TAcc>; +export function createMetadataKey( + reducer?: MetadataReducer, +): MetadataKey, TWrite, TAcc> { + return new (MetadataKey as new ( + reducer: MetadataReducer, + ) => MetadataKey, TWrite, TAcc>)(reducer ?? MetadataReducer.override()); +} + +/** + * Creates a metadata key that exposes a managed value based on the accumulated result of the values + * written to the key. The accumulated value takes the last value set on a given field tree node, + * overriding any previously set values. + * + * @param create A function that receives a signal of the accumulated value and returns the managed + * value based on it. This function runs during the construction of the `FieldTree` node, + * and runs in the injection context of that node. + * @template TRead The type read from the `FieldState` for this key + * @template TWrite The type written to this key using the `metadata()` rule + * + * @experimental 21.0.0 + */ +export function createManagedMetadataKey( + create: (s: Signal) => TRead, +): MetadataKey; +/** + * Creates a metadata key that exposes a managed value based on the accumulated result of the values + * written to the key. + * + * @param create A function that receives a signal of the accumulated value and returns the managed + * value based on it. This function runs during the construction of the `FieldTree` node, + * and runs in the injection context of that node. + * @param reducer The reducer used to combine individual value written to the key, + * this will determine the accumulated value that the create function receives. + * @template TRead The type read from the `FieldState` for this key + * @template TWrite The type written to this key using the `metadata()` rule + * @template TAcc The type of the reducer's accumulated value. + * + * @experimental 21.0.0 + */ +export function createManagedMetadataKey( + create: (s: Signal) => TRead, + reducer: MetadataReducer, +): MetadataKey; +export function createManagedMetadataKey( + create: (s: Signal) => TRead, + reducer?: MetadataReducer, +): MetadataKey { + return new (MetadataKey as new ( + reducer: MetadataReducer, + create: (s: Signal) => TRead, + ) => MetadataKey)(reducer ?? MetadataReducer.override(), create); +} + +/** + * A {@link MetadataKey} representing whether the field is required. + * + * @category validation + * @experimental 21.0.0 + */ +export const REQUIRED: MetadataKey, boolean, boolean> = createMetadataKey( + MetadataReducer.or(), +); + +/** + * A {@link MetadataKey} representing the min value of the field. + * + * @category validation + * @experimental 21.0.0 + */ +export const MIN: MetadataKey< + Signal, + number | undefined, + number | undefined +> = createMetadataKey(MetadataReducer.max()); + +/** + * A {@link MetadataKey} representing the max value of the field. + * + * @category validation + * @experimental 21.0.0 + */ +export const MAX: MetadataKey< + Signal, + number | undefined, + number | undefined +> = createMetadataKey(MetadataReducer.min()); + +/** + * A {@link MetadataKey} representing the min length of the field. + * + * @category validation + * @experimental 21.0.0 + */ +export const MIN_LENGTH: MetadataKey< + Signal, + number | undefined, + number | undefined +> = createMetadataKey(MetadataReducer.max()); + +/** + * A {@link MetadataKey} representing the max length of the field. + * + * @category validation + * @experimental 21.0.0 + */ +export const MAX_LENGTH: MetadataKey< + Signal, + number | undefined, + number | undefined +> = createMetadataKey(MetadataReducer.min()); + +/** + * A {@link MetadataKey} representing the patterns the field must match. + * + * @category validation + * @experimental 21.0.0 + */ +export const PATTERN: MetadataKey< + Signal, + RegExp | undefined, + RegExp[] +> = createMetadataKey(MetadataReducer.list()); diff --git a/packages/forms/signals/src/api/rules/readonly.ts b/packages/forms/signals/src/api/rules/readonly.ts new file mode 100644 index 000000000000..f006ec08e831 --- /dev/null +++ b/packages/forms/signals/src/api/rules/readonly.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {FieldPathNode} from '../../schema/path_node'; +import {assertPathIsCurrent} from '../../schema/schema'; +import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; + +/** + * Adds logic to a field to conditionally make it readonly. A readonly field does not contribute to + * the validation, touched/dirty, or other state of its parent field. + * + * @param path The target path to make readonly. + * @param logic A reactive function that returns `true` when the field is readonly. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function readonly( + path: SchemaPath, + logic: NoInfer> = () => true, +) { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addReadonlyRule(logic); +} diff --git a/packages/forms/signals/src/api/validators/email.ts b/packages/forms/signals/src/api/rules/validation/email.ts similarity index 95% rename from packages/forms/signals/src/api/validators/email.ts rename to packages/forms/signals/src/api/rules/validation/email.ts index 979f3a5d9331..be1371e2ac09 100644 --- a/packages/forms/signals/src/api/validators/email.ts +++ b/packages/forms/signals/src/api/rules/validation/email.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -import {validate} from '../logic'; -import {SchemaPath, SchemaPathRules, PathKind} from '../types'; -import {emailError} from '../validation_errors'; +import {PathKind, SchemaPath, SchemaPathRules} from '../../types'; import {BaseValidatorConfig, getOption, isEmpty} from './util'; +import {validate} from './validate'; +import {emailError} from './validation_errors'; /** * A regular expression that matches valid e-mail addresses. diff --git a/packages/forms/signals/src/api/validators/index.ts b/packages/forms/signals/src/api/rules/validation/index.ts similarity index 72% rename from packages/forms/signals/src/api/validators/index.ts rename to packages/forms/signals/src/api/rules/validation/index.ts index fb68102cefbf..542120ce3139 100644 --- a/packages/forms/signals/src/api/validators/index.ts +++ b/packages/forms/signals/src/api/rules/validation/index.ts @@ -14,3 +14,8 @@ export * from './min_length'; export * from './pattern'; export * from './required'; export * from './standard_schema'; +export * from './validate'; +export * from './validate_async'; +export * from './validate_http'; +export * from './validate_tree'; +export * from './validation_errors'; diff --git a/packages/forms/signals/src/api/validators/max.ts b/packages/forms/signals/src/api/rules/validation/max.ts similarity index 84% rename from packages/forms/signals/src/api/validators/max.ts rename to packages/forms/signals/src/api/rules/validation/max.ts index f60ddfaaba42..af719b840cc7 100644 --- a/packages/forms/signals/src/api/validators/max.ts +++ b/packages/forms/signals/src/api/rules/validation/max.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {MAX} from '../metadata'; -import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; -import {maxError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, MAX, metadata} from '../metadata'; import {BaseValidatorConfig, getOption, isEmpty} from './util'; +import {validate} from './validate'; +import {maxError} from './validation_errors'; /** * Binds a validator to the given path that requires the value to be less than or equal to the @@ -34,10 +33,10 @@ export function max( maxValue: number | LogicFn, config?: BaseValidatorConfig, ) { - const MAX_MEMO = metadata(path, (ctx) => - computed(() => (typeof maxValue === 'number' ? maxValue : maxValue(ctx))), + const MAX_MEMO = metadata(path, createMetadataKey(), (ctx) => + typeof maxValue === 'number' ? maxValue : maxValue(ctx), ); - aggregateMetadata(path, MAX, ({state}) => state.metadata(MAX_MEMO)!()); + metadata(path, MAX, ({state}) => state.metadata(MAX_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { return undefined; diff --git a/packages/forms/signals/src/api/validators/max_length.ts b/packages/forms/signals/src/api/rules/validation/max_length.ts similarity index 80% rename from packages/forms/signals/src/api/validators/max_length.ts rename to packages/forms/signals/src/api/rules/validation/max_length.ts index c0f8f5e1157a..95f5027ca36c 100644 --- a/packages/forms/signals/src/api/validators/max_length.ts +++ b/packages/forms/signals/src/api/rules/validation/max_length.ts @@ -6,11 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {MAX_LENGTH} from '../metadata'; -import {SchemaPath, SchemaPathRules, LogicFn, PathKind} from '../types'; -import {maxLengthError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, MAX_LENGTH, metadata} from '../metadata'; import { BaseValidatorConfig, getLengthOrSize, @@ -18,6 +15,8 @@ import { isEmpty, ValueWithLengthOrSize, } from './util'; +import {validate} from './validate'; +import {maxLengthError} from './validation_errors'; /** * Binds a validator to the given path that requires the length of the value to be less than or @@ -44,10 +43,10 @@ export function maxLength< maxLength: number | LogicFn, config?: BaseValidatorConfig, ) { - const MAX_LENGTH_MEMO = metadata(path, (ctx) => - computed(() => (typeof maxLength === 'number' ? maxLength : maxLength(ctx))), + const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => + typeof maxLength === 'number' ? maxLength : maxLength(ctx), ); - aggregateMetadata(path, MAX_LENGTH, ({state}) => state.metadata(MAX_LENGTH_MEMO)!()); + metadata(path, MAX_LENGTH, ({state}) => state.metadata(MAX_LENGTH_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { return undefined; diff --git a/packages/forms/signals/src/api/validators/min.ts b/packages/forms/signals/src/api/rules/validation/min.ts similarity index 84% rename from packages/forms/signals/src/api/validators/min.ts rename to packages/forms/signals/src/api/rules/validation/min.ts index 74591f96737a..6efa18b5105f 100644 --- a/packages/forms/signals/src/api/validators/min.ts +++ b/packages/forms/signals/src/api/rules/validation/min.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {MIN} from '../metadata'; -import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types'; -import {minError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, metadata, MIN} from '../metadata'; import {BaseValidatorConfig, getOption, isEmpty} from './util'; +import {validate} from './validate'; +import {minError} from './validation_errors'; /** * Binds a validator to the given path that requires the value to be greater than or equal to @@ -37,10 +36,10 @@ export function min< minValue: number | LogicFn, config?: BaseValidatorConfig, ) { - const MIN_MEMO = metadata(path, (ctx) => - computed(() => (typeof minValue === 'number' ? minValue : minValue(ctx))), + const MIN_MEMO = metadata(path, createMetadataKey(), (ctx) => + typeof minValue === 'number' ? minValue : minValue(ctx), ); - aggregateMetadata(path, MIN, ({state}) => state.metadata(MIN_MEMO)!()); + metadata(path, MIN, ({state}) => state.metadata(MIN_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { return undefined; diff --git a/packages/forms/signals/src/api/validators/min_length.ts b/packages/forms/signals/src/api/rules/validation/min_length.ts similarity index 80% rename from packages/forms/signals/src/api/validators/min_length.ts rename to packages/forms/signals/src/api/rules/validation/min_length.ts index 4a22f2e8b85b..f8ee9ef9c322 100644 --- a/packages/forms/signals/src/api/validators/min_length.ts +++ b/packages/forms/signals/src/api/rules/validation/min_length.ts @@ -6,11 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {MIN_LENGTH} from '../metadata'; -import {SchemaPath, LogicFn, PathKind, SchemaPathRules} from '../types'; -import {minLengthError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, metadata, MIN_LENGTH} from '../metadata'; import { BaseValidatorConfig, getLengthOrSize, @@ -18,6 +15,8 @@ import { isEmpty, ValueWithLengthOrSize, } from './util'; +import {validate} from './validate'; +import {minLengthError} from './validation_errors'; /** * Binds a validator to the given path that requires the length of the value to be greater than or @@ -44,10 +43,10 @@ export function minLength< minLength: number | LogicFn, config?: BaseValidatorConfig, ) { - const MIN_LENGTH_MEMO = metadata(path, (ctx) => - computed(() => (typeof minLength === 'number' ? minLength : minLength(ctx))), + const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), (ctx) => + typeof minLength === 'number' ? minLength : minLength(ctx), ); - aggregateMetadata(path, MIN_LENGTH, ({state}) => state.metadata(MIN_LENGTH_MEMO)!()); + metadata(path, MIN_LENGTH, ({state}) => state.metadata(MIN_LENGTH_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { return undefined; diff --git a/packages/forms/signals/src/api/validators/pattern.ts b/packages/forms/signals/src/api/rules/validation/pattern.ts similarity index 79% rename from packages/forms/signals/src/api/validators/pattern.ts rename to packages/forms/signals/src/api/rules/validation/pattern.ts index e56b3bc1c3c7..ff6bad05d2fa 100644 --- a/packages/forms/signals/src/api/validators/pattern.ts +++ b/packages/forms/signals/src/api/rules/validation/pattern.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {PATTERN} from '../metadata'; -import {SchemaPath, LogicFn, PathKind, SchemaPathRules} from '../types'; -import {patternError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, metadata, PATTERN} from '../metadata'; import {BaseValidatorConfig, getOption, isEmpty} from './util'; +import {validate} from './validate'; +import {patternError} from './validation_errors'; /** * Binds a validator to the given path that requires the value to match a specific regex pattern. @@ -33,10 +32,10 @@ export function pattern( pattern: RegExp | LogicFn, config?: BaseValidatorConfig, ) { - const PATTERN_MEMO = metadata(path, (ctx) => - computed(() => (pattern instanceof RegExp ? pattern : pattern(ctx))), + const PATTERN_MEMO = metadata(path, createMetadataKey(), (ctx) => + pattern instanceof RegExp ? pattern : pattern(ctx), ); - aggregateMetadata(path, PATTERN, ({state}) => state.metadata(PATTERN_MEMO)!()); + metadata(path, PATTERN, ({state}) => state.metadata(PATTERN_MEMO)!()); validate(path, (ctx) => { if (isEmpty(ctx.value())) { return undefined; diff --git a/packages/forms/signals/src/api/validators/required.ts b/packages/forms/signals/src/api/rules/validation/required.ts similarity index 79% rename from packages/forms/signals/src/api/validators/required.ts rename to packages/forms/signals/src/api/rules/validation/required.ts index 84700ab16b88..2414ab175a47 100644 --- a/packages/forms/signals/src/api/validators/required.ts +++ b/packages/forms/signals/src/api/rules/validation/required.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed} from '@angular/core'; -import {aggregateMetadata, metadata, validate} from '../logic'; -import {REQUIRED} from '../metadata'; -import {SchemaPath, LogicFn, PathKind, SchemaPathRules} from '../types'; -import {requiredError} from '../validation_errors'; +import {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../../types'; +import {createMetadataKey, metadata, REQUIRED} from '../metadata'; import {BaseValidatorConfig, getOption, isEmpty} from './util'; +import {validate} from './validate'; +import {requiredError} from './validation_errors'; /** * Binds a validator to the given path that requires the value to be non-empty. @@ -36,10 +35,10 @@ export function required( when?: NoInfer>; }, ): void { - const REQUIRED_MEMO = metadata(path, (ctx) => - computed(() => (config?.when ? config.when(ctx) : true)), + const REQUIRED_MEMO = metadata(path, createMetadataKey(), (ctx) => + config?.when ? config.when(ctx) : true, ); - aggregateMetadata(path, REQUIRED, ({state}) => state.metadata(REQUIRED_MEMO)!()); + metadata(path, REQUIRED, ({state}) => state.metadata(REQUIRED_MEMO)!()!); validate(path, (ctx) => { if (ctx.state.metadata(REQUIRED_MEMO)!() && isEmpty(ctx.value())) { if (config?.error) { diff --git a/packages/forms/signals/src/api/validators/standard_schema.ts b/packages/forms/signals/src/api/rules/validation/standard_schema.ts similarity index 84% rename from packages/forms/signals/src/api/validators/standard_schema.ts rename to packages/forms/signals/src/api/rules/validation/standard_schema.ts index 7ec8998512d9..683cec510916 100644 --- a/packages/forms/signals/src/api/validators/standard_schema.ts +++ b/packages/forms/signals/src/api/rules/validation/standard_schema.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed, resource, ɵisPromise, Signal} from '@angular/core'; +import {resource, ɵisPromise} from '@angular/core'; import type {StandardSchemaV1} from '@standard-schema/spec'; -import {addDefaultField} from '../../field/validation'; -import {validateAsync} from '../async'; -import {metadata, validateTree} from '../logic'; -import type {SchemaPath, SchemaPathTree, FieldTree} from '../types'; -import {standardSchemaError, StandardSchemaValidationError} from '../validation_errors'; +import {addDefaultField} from '../../../field/validation'; +import type {FieldTree, SchemaPath, SchemaPathTree} from '../../types'; +import {createMetadataKey, metadata} from '../metadata'; +import {validateAsync} from './validate_async'; +import {validateTree} from './validate_tree'; +import {standardSchemaError, StandardSchemaValidationError} from './validation_errors'; /** * Utility type that removes a string index key when its value is `unknown`, @@ -65,9 +66,13 @@ export function validateStandardSchema | Promise>; - const VALIDATOR_MEMO = metadata>(path, ({value}) => { - return computed(() => schema['~standard'].validate(value())); - }); + const VALIDATOR_MEMO = metadata( + path as SchemaPath, + createMetadataKey(), + ({value}) => { + return schema['~standard'].validate(value()); + }, + ); validateTree(path, ({state, fieldTreeOf}) => { // Skip sync validation if the result is a Promise. @@ -76,7 +81,7 @@ export function validateStandardSchema + result?.issues?.map((issue) => standardIssueToFormTreeError(fieldTreeOf(path), issue), ) ?? [] ); @@ -108,15 +113,15 @@ export function validateStandardSchema, + fieldTree: FieldTree, issue: StandardSchemaV1.Issue, ): StandardSchemaValidationError { - let target = field as FieldTree>; + let target = fieldTree as FieldTree>; for (const pathPart of issue.path ?? []) { const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart; target = target[pathKey] as FieldTree>; diff --git a/packages/forms/signals/src/api/validators/util.ts b/packages/forms/signals/src/api/rules/validation/util.ts similarity index 95% rename from packages/forms/signals/src/api/validators/util.ts rename to packages/forms/signals/src/api/rules/validation/util.ts index 7646ed5a9271..03f6ad7dfe8e 100644 --- a/packages/forms/signals/src/api/validators/util.ts +++ b/packages/forms/signals/src/api/rules/validation/util.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ -import {isArray} from '../../util/type_guards'; -import {LogicFn, OneOrMany, PathKind, ValidationResult, type FieldContext} from '../types'; -import {customError, ValidationError} from '../validation_errors'; +import {isArray} from '../../../util/type_guards'; +import {LogicFn, OneOrMany, PathKind, ValidationResult, type FieldContext} from '../../types'; +import {customError, ValidationError} from './validation_errors'; /** Represents a value that has a length or size, such as an array or string, or set. */ export type ValueWithLengthOrSize = {length: number} | {size: number}; diff --git a/packages/forms/signals/src/api/rules/validation/validate.ts b/packages/forms/signals/src/api/rules/validation/validate.ts new file mode 100644 index 000000000000..e19a44f73f5c --- /dev/null +++ b/packages/forms/signals/src/api/rules/validation/validate.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {addDefaultField} from '../../../field/validation'; +import {FieldPathNode} from '../../../schema/path_node'; +import {assertPathIsCurrent} from '../../../schema/schema'; +import type { + FieldContext, + FieldValidator, + PathKind, + SchemaPath, + SchemaPathRules, +} from '../../types'; +import {ensureCustomValidationResult} from './util'; + +/** + * Adds logic to a field to determine if the field has validation errors. + * + * @param path The target path to add the validation logic to. + * @param logic A `Validator` that returns the current validation errors. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function validate( + path: SchemaPath, + logic: NoInfer>, +): void { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addSyncErrorRule((ctx) => { + return ensureCustomValidationResult( + addDefaultField(logic(ctx as FieldContext), ctx.fieldTree), + ); + }); +} diff --git a/packages/forms/signals/src/api/async.ts b/packages/forms/signals/src/api/rules/validation/validate_async.ts similarity index 55% rename from packages/forms/signals/src/api/async.ts rename to packages/forms/signals/src/api/rules/validation/validate_async.ts index 27d81bd89cd6..22d2f3a8168b 100644 --- a/packages/forms/signals/src/api/async.ts +++ b/packages/forms/signals/src/api/rules/validation/validate_async.ts @@ -6,14 +6,19 @@ * found in the LICENSE file at https://angular.dev/license */ -import {httpResource, HttpResourceOptions, HttpResourceRequest} from '@angular/common/http'; -import {computed, ResourceRef, Signal} from '@angular/core'; -import {FieldNode} from '../field/node'; -import {addDefaultField} from '../field/validation'; -import {FieldPathNode} from '../schema/path_node'; -import {assertPathIsCurrent} from '../schema/schema'; -import {metadata} from './logic'; -import {FieldContext, SchemaPath, PathKind, TreeValidationResult, SchemaPathRules} from './types'; +import {ResourceRef, Signal} from '@angular/core'; +import {FieldNode} from '../../../field/node'; +import {addDefaultField} from '../../../field/validation'; +import {FieldPathNode} from '../../../schema/path_node'; +import {assertPathIsCurrent} from '../../../schema/schema'; +import { + FieldContext, + PathKind, + SchemaPath, + SchemaPathRules, + TreeValidationResult, +} from '../../types'; +import {createManagedMetadataKey, metadata} from '../metadata'; /** * A function that takes the result of an async operation and the current field context, and maps it @@ -90,53 +95,6 @@ export interface AsyncValidatorOptions< readonly onSuccess: MapToErrorsFn; } -/** - * Options that indicate how to create an httpResource for async validation for a field, - * and map its result to validation errors. - * - * @template TValue The type of value stored in the field being validated. - * @template TResult The type of result returned by the httpResource - * @template TPathKind The kind of path being validated (a root path, child path, or item of an array) - * - * @category validation - * @experimental 21.0.0 - */ -export interface HttpValidatorOptions { - /** - * A function that receives the field context and returns the url or request for the httpResource. - * If given a URL, the underlying httpResource will perform an HTTP GET on it. - * - * @param ctx The field context for the field being validated. - * @returns The URL or request for creating the httpResource. - */ - readonly request: - | ((ctx: FieldContext) => string | undefined) - | ((ctx: FieldContext) => HttpResourceRequest | undefined); - - /** - * A function that takes the httpResource result, and the current field context and maps it to a - * list of validation errors. - * - * @param result The httpResource result. - * @param ctx The context for the field the validator is attached to. - * @return A validation error, or list of validation errors to report based on the httpResource result. - * The returned errors can optionally specify a field that the error should be targeted to. - * A targeted error will show up as an error on its target field rather than the field being validated. - * If a field is not given, the error is assumed to apply to the field being validated. - */ - readonly onSuccess: MapToErrorsFn; - - /** - * A function to handle errors thrown by httpResource (HTTP errors, network errors, etc.). - * Receives the error and the field context, returns a list of validation errors. - */ - readonly onError: (error: unknown, ctx: FieldContext) => TreeValidationResult; - /** - * The options to use when creating the httpResource. - */ - readonly options?: HttpResourceOptions; -} - /** * Adds async validation to the field corresponding to the given path based on a resource. * Async validation for a field only runs once all synchronous validation is passing. @@ -158,16 +116,16 @@ export function validateAsync { - const params = computed(() => { - const node = ctx.stateOf(path) as FieldNode; - const validationState = node.validationState; - if (validationState.shouldSkipValidation() || !validationState.syncValid()) { - return undefined; - } - return opts.params(ctx); - }); - return opts.factory(params); + const RESOURCE = createManagedMetadataKey, TParams | undefined>( + opts.factory, + ); + metadata(path, RESOURCE, (ctx) => { + const node = ctx.stateOf(path) as FieldNode; + const validationState = node.validationState; + if (validationState.shouldSkipValidation() || !validationState.syncValid()) { + return undefined; + } + return opts.params(ctx); }); pathNode.builder.addAsyncErrorRule((ctx) => { @@ -185,35 +143,10 @@ export function validateAsync); - return addDefaultField(errors, ctx.field); + return addDefaultField(errors, ctx.fieldTree); case 'error': errors = opts.onError(res.error(), ctx as FieldContext); - return addDefaultField(errors, ctx.field); + return addDefaultField(errors, ctx.fieldTree); } }); } - -/** - * Adds async validation to the field corresponding to the given path based on an httpResource. - * Async validation for a field only runs once all synchronous validation is passing. - * - * @param path A path indicating the field to bind the async validation logic to. - * @param opts The http validation options. - * @template TValue The type of value stored in the field being validated. - * @template TResult The type of result returned by the httpResource - * @template TPathKind The kind of path being validated (a root path, child path, or item of an array) - * - * @category validation - * @experimental 21.0.0 - */ -export function validateHttp( - path: SchemaPath, - opts: HttpValidatorOptions, -) { - validateAsync(path, { - params: opts.request, - factory: (request: Signal) => httpResource(request, opts.options), - onSuccess: opts.onSuccess, - onError: opts.onError, - }); -} diff --git a/packages/forms/signals/src/api/rules/validation/validate_http.ts b/packages/forms/signals/src/api/rules/validation/validate_http.ts new file mode 100644 index 000000000000..926a612a7c33 --- /dev/null +++ b/packages/forms/signals/src/api/rules/validation/validate_http.ts @@ -0,0 +1,90 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {httpResource, HttpResourceOptions, HttpResourceRequest} from '@angular/common/http'; +import {Signal} from '@angular/core'; +import { + FieldContext, + SchemaPath, + PathKind, + TreeValidationResult, + SchemaPathRules, +} from '../../types'; +import {MapToErrorsFn, validateAsync} from './validate_async'; + +/** + * Options that indicate how to create an httpResource for async validation for a field, + * and map its result to validation errors. + * + * @template TValue The type of value stored in the field being validated. + * @template TResult The type of result returned by the httpResource + * @template TPathKind The kind of path being validated (a root path, child path, or item of an array) + * + * @category validation + * @experimental 21.0.0 + */ +export interface HttpValidatorOptions { + /** + * A function that receives the field context and returns the url or request for the httpResource. + * If given a URL, the underlying httpResource will perform an HTTP GET on it. + * + * @param ctx The field context for the field being validated. + * @returns The URL or request for creating the httpResource. + */ + readonly request: + | ((ctx: FieldContext) => string | undefined) + | ((ctx: FieldContext) => HttpResourceRequest | undefined); + + /** + * A function that takes the httpResource result, and the current field context and maps it to a + * list of validation errors. + * + * @param result The httpResource result. + * @param ctx The context for the field the validator is attached to. + * @return A validation error, or list of validation errors to report based on the httpResource result. + * The returned errors can optionally specify a field that the error should be targeted to. + * A targeted error will show up as an error on its target field rather than the field being validated. + * If a field is not given, the error is assumed to apply to the field being validated. + */ + readonly onSuccess: MapToErrorsFn; + + /** + * A function to handle errors thrown by httpResource (HTTP errors, network errors, etc.). + * Receives the error and the field context, returns a list of validation errors. + */ + readonly onError: (error: unknown, ctx: FieldContext) => TreeValidationResult; + /** + * The options to use when creating the httpResource. + */ + readonly options?: HttpResourceOptions; +} + +/** + * Adds async validation to the field corresponding to the given path based on an httpResource. + * Async validation for a field only runs once all synchronous validation is passing. + * + * @param path A path indicating the field to bind the async validation logic to. + * @param opts The http validation options. + * @template TValue The type of value stored in the field being validated. + * @template TResult The type of result returned by the httpResource + * @template TPathKind The kind of path being validated (a root path, child path, or item of an array) + * + * @category validation + * @experimental 21.0.0 + */ +export function validateHttp( + path: SchemaPath, + opts: HttpValidatorOptions, +) { + validateAsync(path, { + params: opts.request, + factory: (request: Signal) => httpResource(request, opts.options), + onSuccess: opts.onSuccess, + onError: opts.onError, + }); +} diff --git a/packages/forms/signals/src/api/rules/validation/validate_tree.ts b/packages/forms/signals/src/api/rules/validation/validate_tree.ts new file mode 100644 index 000000000000..6ee1cf08a44f --- /dev/null +++ b/packages/forms/signals/src/api/rules/validation/validate_tree.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {addDefaultField} from '../../../field/validation'; +import {FieldPathNode} from '../../../schema/path_node'; +import {assertPathIsCurrent} from '../../../schema/schema'; +import type {FieldContext, PathKind, SchemaPath, SchemaPathRules, TreeValidator} from '../../types'; + +/** + * Adds logic to a field to determine if the field or any of its child fields has validation errors. + * + * @param path The target path to add the validation logic to. + * @param logic A `TreeValidator` that returns the current validation errors. + * Errors returned by the validator may specify a target field to indicate an error on a child field. + * @template TValue The type of value stored in the field the logic is bound to. + * @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array) + * + * @category logic + * @experimental 21.0.0 + */ +export function validateTree( + path: SchemaPath, + logic: NoInfer>, +): void { + assertPathIsCurrent(path); + + const pathNode = FieldPathNode.unwrapFieldPath(path); + pathNode.builder.addSyncTreeErrorRule((ctx) => + addDefaultField(logic(ctx as FieldContext), ctx.fieldTree), + ); +} diff --git a/packages/forms/signals/src/api/validation_errors.ts b/packages/forms/signals/src/api/rules/validation/validation_errors.ts similarity index 97% rename from packages/forms/signals/src/api/validation_errors.ts rename to packages/forms/signals/src/api/rules/validation/validation_errors.ts index c1e3705150e3..beb42abd1ccb 100644 --- a/packages/forms/signals/src/api/validation_errors.ts +++ b/packages/forms/signals/src/api/rules/validation/validation_errors.ts @@ -7,7 +7,7 @@ */ import type {StandardSchemaV1} from '@standard-schema/spec'; -import {FieldTree} from './types'; +import {FieldTree} from '../../types'; /** * Options used to create a `ValidationError`. @@ -23,7 +23,7 @@ interface ValidationErrorOptions { * * @experimental 21.0.0 */ -export type WithField = T & {field: FieldTree}; +export type WithField = T & {fieldTree: FieldTree}; /** * A type that allows the given type `T` to optionally have a `field` property. @@ -31,7 +31,7 @@ export type WithField = T & {field: FieldTree}; * * @experimental 21.0.0 */ -export type WithOptionalField = Omit & {field?: FieldTree}; +export type WithOptionalField = Omit & {fieldTree?: FieldTree}; /** * A type that ensures the given type `T` does not have a `field` property. @@ -39,7 +39,7 @@ export type WithOptionalField = Omit & {field?: FieldTree = T & {field: never}; +export type WithoutField = T & {fieldTree: never}; /** * Create a required error associated with the target field @@ -325,7 +325,7 @@ export declare namespace ValidationError { */ export interface WithField extends ValidationError { /** The field associated with this error. */ - readonly field: FieldTree; + readonly fieldTree: FieldTree; } /** @@ -336,7 +336,7 @@ export declare namespace ValidationError { */ export interface WithOptionalField extends ValidationError { /** The field associated with this error. */ - readonly field?: FieldTree; + readonly fieldTree?: FieldTree; } /** @@ -369,7 +369,7 @@ export class CustomValidationError implements ValidationError { readonly kind: string = ''; /** The field associated with this error. */ - readonly field!: FieldTree; + readonly fieldTree!: FieldTree; /** Human readable error message. */ readonly message?: string; @@ -395,7 +395,7 @@ abstract class _NgValidationError implements ValidationError { readonly kind: string = ''; /** The field associated with this error. */ - readonly field!: FieldTree; + readonly fieldTree!: FieldTree; /** Human readable error message. */ readonly message?: string; @@ -534,7 +534,7 @@ export class StandardSchemaValidationError extends _NgValidationError { * is one of the standard kinds, allowing you to switch on the kind to further narrow the type. * * @example - * ``` + * ```ts * const f = form(...); * for (const e of form().errors()) { * if (e instanceof NgValidationError) { diff --git a/packages/forms/signals/src/api/structure.ts b/packages/forms/signals/src/api/structure.ts index 3fdb40ae7881..72b2b2cecd86 100644 --- a/packages/forms/signals/src/api/structure.ts +++ b/packages/forms/signals/src/api/structure.ts @@ -17,6 +17,7 @@ import {FieldPathNode} from '../schema/path_node'; import {assertPathIsCurrent, SchemaImpl} from '../schema/schema'; import {normalizeFormArgs} from '../util/normalize_form_args'; import {isArray} from '../util/type_guards'; +import type {ValidationError} from './rules/validation/validation_errors'; import type { FieldTree, ItemType, @@ -29,7 +30,6 @@ import type { SchemaPath, TreeValidationResult, } from './types'; -import type {ValidationError} from './validation_errors'; /** * Options that may be specified when creating a form. @@ -61,7 +61,7 @@ export interface FormOptions { * as well. * * @example - * ``` + * ```ts * const nameModel = signal({first: '', last: ''}); * const nameForm = form(nameModel); * nameForm.first().value.set('John'); @@ -89,7 +89,7 @@ export function form(model: WritableSignal): FieldTree; * as well. * * @example - * ``` + * ```ts * const nameModel = signal({first: '', last: ''}); * const nameForm = form(nameModel); * nameForm.first().value.set('John'); @@ -102,7 +102,7 @@ export function form(model: WritableSignal): FieldTree; * function that builds the schema by binding logic to a parts of the field structure. * * @example - * ``` + * ```ts * const nameForm = form(signal({first: '', last: ''}), (name) => { * required(name.first); * pattern(name.last, /^[a-z]+$/i, {message: 'Alphabet characters only'}); @@ -139,7 +139,7 @@ export function form( * as well. * * @example - * ``` + * ```ts * const nameModel = signal({first: '', last: ''}); * const nameForm = form(nameModel); * nameForm.first().value.set('John'); @@ -152,7 +152,7 @@ export function form( * function that builds the schema by binding logic to a parts of the field structure. * * @example - * ``` + * ```ts * const nameForm = form(signal({first: '', last: ''}), (name) => { * required(name.first); * validate(name.last, ({value}) => !/^[a-z]+$/i.test(value()) ? customError({kind: 'alphabet-only'}) : undefined); @@ -195,7 +195,7 @@ export function form(...args: any[]): FieldTree { * Applies a schema to each item of an array. * * @example - * ``` + * ```ts * const nameSchema = schema<{first: string, last: string}>((name) => { * required(name.first); * required(name.last); @@ -235,7 +235,7 @@ export function applyEach( * Applies a predefined schema to a given `FieldPath`. * * @example - * ``` + * ```ts * const nameSchema = schema<{first: string, last: string}>((name) => { * required(name.first); * required(name.last); @@ -330,13 +330,13 @@ export function applyWhenValue( } /** - * Submits a given `FieldTree` using the given action function and applies any server errors - * resulting from the action to the field. Server errors returned by the `action` will be integrated + * Submits a given `FieldTree` using the given action function and applies any submission errors + * resulting from the action to the field. Submission errors returned by the `action` will be integrated * into the field as a `ValidationError` on the sub-field indicated by the `field` property of the - * server error. + * submission error. * * @example - * ``` + * ```ts * async function registerNewUser(registrationForm: FieldTree<{username: string, password: string}>) { * const result = await myClient.registerNewUser(registrationForm().value()); * if (result.errorCode === myClient.ErrorCode.USERNAME_TAKEN) { @@ -356,7 +356,7 @@ export function applyWhenValue( * ``` * * @param form The field to submit. - * @param action An asynchronous action used to submit the field. The action may return server + * @param action An asynchronous action used to submit the field. The action may return submission * errors. * @template TModel The data type of the field being submitted. * @@ -378,19 +378,19 @@ export async function submit( node.submitState.selfSubmitting.set(true); try { const errors = await action(form); - errors && setServerErrors(node, errors); + errors && setSubmissionErrors(node, errors); } finally { node.submitState.selfSubmitting.set(false); } } /** - * Sets a list of server errors to their individual fields. + * Sets a list of submission errors to their individual fields. * * @param submittedField The field that was submitted, resulting in the errors. * @param errors The errors to set. */ -function setServerErrors( +function setSubmissionErrors( submittedField: FieldNode, errors: OneOrMany, ) { @@ -400,7 +400,7 @@ function setServerErrors( const errorsByField = new Map(); for (const error of errors) { const errorWithField = addDefaultField(error, submittedField.fieldProxy); - const field = errorWithField.field() as FieldNode; + const field = errorWithField.fieldTree() as FieldNode; let fieldErrors = errorsByField.get(field); if (!fieldErrors) { fieldErrors = []; @@ -409,7 +409,7 @@ function setServerErrors( fieldErrors.push(errorWithField); } for (const [field, fieldErrors] of errorsByField) { - field.submitState.serverErrors.set(fieldErrors); + field.submitState.submissionErrors.set(fieldErrors); } } diff --git a/packages/forms/signals/src/api/types.ts b/packages/forms/signals/src/api/types.ts index ccbc4296faa2..08130834b4d1 100644 --- a/packages/forms/signals/src/api/types.ts +++ b/packages/forms/signals/src/api/types.ts @@ -9,8 +9,8 @@ import {Signal, ɵFieldState} from '@angular/core'; import {AbstractControl} from '@angular/forms'; import type {Field} from './field_directive'; -import {AggregateMetadataKey, MetadataKey} from './metadata'; -import type {ValidationError} from './validation_errors'; +import type {MetadataKey} from './rules/metadata'; +import type {ValidationError} from './rules/validation/validation_errors'; /** * Symbol used to retain generic type information when it would otherwise be lost. @@ -75,7 +75,7 @@ export type SubmittedStatus = 'unsubmitted' | 'submitted' | 'submitting'; */ export interface DisabledReason { /** The field that is disabled. */ - readonly field: FieldTree; + readonly fieldTree: FieldTree; /** A user-facing message describing the reason for the disablement. */ readonly message?: string; } @@ -297,22 +297,11 @@ export interface FieldState[]>; - /** - * Reads an aggregate metadata value from the field. - * @param key The metadata key to read. - */ - metadata(key: AggregateMetadataKey): Signal; - /** * Reads a metadata value from the field. * @param key The metadata key to read. */ - metadata(key: MetadataKey): M | undefined; - - /** - * Checks whether the given metadata key has been defined for this field. - */ - hasMetadata(key: MetadataKey | AggregateMetadataKey): boolean; + metadata(key: MetadataKey): M | undefined; /** * Resets the {@link touched} and {@link dirty} state of the field and its descendants. @@ -444,9 +433,42 @@ export type MaybeSchemaPathTree, TPathKind>; /** - * Defines logic for a form. + * A reusable schema that defines behavior and rules for a form. + * + * A `Schema` encapsulates form logic such as validation rules, disabled states, readonly states, + * and other field-level behaviors. + * + * Unlike raw {@link SchemaFn}, a `Schema` is created using + * the {@link schema} function and is cached per-form, even when applied to multiple fields. + * + * ### Creating a reusable schema * - * @template TValue The type of data stored in the form that this schema is attached to. + * ```typescript + * interface Address { + * street: string; + * city: string; + * } + * + * // Create a reusable schema for address fields + * const addressSchema = schema
    ((p) => { + * required(p.street); + * required(p.city); + * }); + * + * // Apply the schema to multiple forms + * const shippingForm = form(shippingModel, addressSchema, {injector}); + * const billingForm = form(billingModel, addressSchema, {injector}); + * ``` + * + * ### Passing a schema to a form + * + * A schema can also be passed as a second argument to the {@link form} function. + * + * ```typescript + * readonly userForm = form(addressModel, addressSchema); + * ``` + * + * @template TModel Data type. * * @category types * @experimental 21.0.0 @@ -456,9 +478,21 @@ export type Schema = { }; /** - * Function that defines rules for a schema. + * A function that receives a {@link SchemaPathTree} and applies rules to fields. * - * @template TModel The type of data stored in the form that this schema function is attached to. + * A `SchemaFn` can be passed directly to {@link form} or to the {@link schema} function to create a + * cached {@link Schema}. + * + * ```typescript + * const userFormSchema: SchemaFn = (p) => { + * required(p.name); + * disabled(p.email, ({valueOf}) => valueOf(p.name) === ''); + * }; + * + * const f = form(userModel, userFormSchema, {injector}); + * ``` + * + * @template TModel Data type. * @template TPathKind The kind of path this schema function can be bound to. * * @category types @@ -469,7 +503,7 @@ export type SchemaFn = ( ) => void; /** - * A schema or schema definition function. + * A {@link Schema} or {@link SchemaFn}. * * @template TModel The type of data stored in the form that this schema function is attached to. * @template TPathKind The kind of path this schema function can be bound to. @@ -572,7 +606,7 @@ export interface RootFieldContext { /** The state of the current field. */ readonly state: FieldState; /** The current field. */ - readonly field: FieldTree; + readonly fieldTree: FieldTree; /** Gets the value of the field represented by the given path. */ valueOf(p: SchemaPath): PValue; diff --git a/packages/forms/signals/src/controls/interop_ng_control.ts b/packages/forms/signals/src/controls/interop_ng_control.ts index cecd13d54528..c8f60ee48434 100644 --- a/packages/forms/signals/src/controls/interop_ng_control.ts +++ b/packages/forms/signals/src/controls/interop_ng_control.ts @@ -6,6 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import {ɵRuntimeError as RuntimeError} from '@angular/core'; +import {SignalFormsErrorCode} from '../errors'; + import { ControlValueAccessor, Validators, @@ -15,7 +18,6 @@ import { type ValidationErrors, type ValidatorFn, } from '@angular/forms'; -import {REQUIRED} from '../api/metadata'; import type {FieldState} from '../api/types'; // TODO: Also consider supporting (if possible): @@ -123,7 +125,10 @@ export class InteropNgControl if (this.field().pending()) { return 'PENDING'; } - throw Error('AssertionError: unknown form control status'); + throw new RuntimeError( + SignalFormsErrorCode.UNKNOWN_STATUS, + ngDevMode && 'Unknown form control status', + ); } valueAccessor: ControlValueAccessor | null = null; @@ -132,7 +137,7 @@ export class InteropNgControl // This addresses a common case where users look for the presence of `Validators.required` to // determine whether or not to show a required "*" indicator in the UI. if (validator === Validators.required) { - return this.field().metadata(REQUIRED)(); + return this.field().required(); } return false; } diff --git a/packages/forms/signals/src/errors.ts b/packages/forms/signals/src/errors.ts new file mode 100644 index 000000000000..28c0d2cc9095 --- /dev/null +++ b/packages/forms/signals/src/errors.ts @@ -0,0 +1,27 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +/** + * The list of error codes used in runtime code of the `forms` package. + * Reserved error code range: 1900-1999. + */ +export const enum SignalFormsErrorCode { + // Signal Forms errors (1900-1999) + PATH_NOT_IN_FIELD_TREE = 1900, + PATH_RESOLUTION_FAILED = 1901, + ORPHAN_FIELD_PROPERTY = 1902, + ORPHAN_FIELD_ARRAY = 1903, + ORPHAN_FIELD_NOT_FOUND = 1904, + ROOT_FIELD_NO_PARENT = 1905, + PARENT_NOT_ARRAY = 1906, + ABSTRACT_CONTROL_IN_FORM = 1907, + PATH_OUTSIDE_SCHEMA = 1908, + UNKNOWN_BUILDER_TYPE = 1909, + UNKNOWN_STATUS = 1910, + COMPAT_NO_CHILDREN = 1911, +} diff --git a/packages/forms/signals/src/field/context.ts b/packages/forms/signals/src/field/context.ts index ceb630ddb4e2..a0521b7331c4 100644 --- a/packages/forms/signals/src/field/context.ts +++ b/packages/forms/signals/src/field/context.ts @@ -6,7 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed, Signal, untracked, WritableSignal} from '@angular/core'; +import { + computed, + Signal, + untracked, + WritableSignal, + ɵRuntimeError as RuntimeError, +} from '@angular/core'; +import {SignalFormsErrorCode} from '../errors'; import {AbstractControl} from '@angular/forms'; import { FieldContext, @@ -65,7 +72,10 @@ export class FieldNodeContext implements FieldContext { stepsRemaining--; field = field.structure.parent; if (field === undefined) { - throw new Error('Path is not part of this field tree.'); + throw new RuntimeError( + SignalFormsErrorCode.PATH_NOT_IN_FIELD_TREE, + ngDevMode && 'Path is not part of this field tree.', + ); } } @@ -74,11 +84,13 @@ export class FieldNodeContext implements FieldContext { for (let key of targetPathNode.keys) { field = field.structure.getChild(key); if (field === undefined) { - throw new Error( - `Cannot resolve path .${targetPathNode.keys.join('.')} relative to field ${[ - '', - ...this.node.structure.pathKeys(), - ].join('.')}.`, + throw new RuntimeError( + SignalFormsErrorCode.PATH_RESOLUTION_FAILED, + ngDevMode && + `Cannot resolve path .${targetPathNode.keys.join('.')} relative to field ${[ + '', + ...this.node.structure.pathKeys(), + ].join('.')}.`, ); } } @@ -91,7 +103,7 @@ export class FieldNodeContext implements FieldContext { return this.cache.get(target)!() as FieldTree; } - get field(): FieldTree { + get fieldTree(): FieldTree { return this.node.fieldProxy; } @@ -116,7 +128,10 @@ export class FieldNodeContext implements FieldContext { const key = this.key(); // Assert that the parent is actually an array. if (!isArray(untracked(this.node.structure.parent!.value))) { - throw new Error(`RuntimeError: cannot access index, parent field is not an array`); + throw new RuntimeError( + SignalFormsErrorCode.PARENT_NOT_ARRAY, + ngDevMode && 'Cannot access index, parent field is not an array.', + ); } // Return the key as a number if we are indeed inside an array field. return Number(key); @@ -128,8 +143,10 @@ export class FieldNodeContext implements FieldContext { const result = this.resolve(p)().value(); if (result instanceof AbstractControl) { - throw new Error( - `Tried to read an 'AbstractControl' value form a 'form()'. Did you mean to use 'compatForm()' instead?`, + throw new RuntimeError( + SignalFormsErrorCode.ABSTRACT_CONTROL_IN_FORM, + ngDevMode && + `Tried to read an 'AbstractControl' value from a 'form()'. Did you mean to use 'compatForm()' instead?`, ); } return result; diff --git a/packages/forms/signals/src/field/debounce.ts b/packages/forms/signals/src/field/debounce.ts index 5d83d2be05a5..06f46b0b88c8 100644 --- a/packages/forms/signals/src/field/debounce.ts +++ b/packages/forms/signals/src/field/debounce.ts @@ -6,18 +6,17 @@ * found in the LICENSE file at https://angular.dev/license */ -import {AggregateMetadataKey, reducedMetadataKey} from '../api/metadata'; +import type {Signal} from '@angular/core'; +import {createMetadataKey, MetadataKey} from '../api/rules/metadata'; import {Debouncer} from '../api/types'; /** - * A private {@link AggregateMetadataKey} used to aggregate `debounce()` rules. + * A private {@link MetadataKey} used to aggregate `debounce()` rules. * * This will pick the last `debounce()` rule on a field that is currently applied, if conditional. */ -export const DEBOUNCER: AggregateMetadataKey< +export const DEBOUNCER: MetadataKey< + Signal | undefined> | undefined, Debouncer | undefined, - Debouncer -> = reducedMetadataKey( - (_, item) => item, - () => undefined, -); + Debouncer | undefined +> = createMetadataKey(); diff --git a/packages/forms/signals/src/field/metadata.ts b/packages/forms/signals/src/field/metadata.ts index e63f77d0a5c1..b0e2e5243ccf 100644 --- a/packages/forms/signals/src/field/metadata.ts +++ b/packages/forms/signals/src/field/metadata.ts @@ -6,65 +6,56 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed, runInInjectionContext, Signal, untracked} from '@angular/core'; -import {AggregateMetadataKey, MetadataKey} from '../api/metadata'; +import {computed, runInInjectionContext, untracked} from '@angular/core'; +import {MetadataKey} from '../api/rules/metadata'; import type {FieldNode} from './node'; -import {cast} from './util'; /** * Tracks custom metadata associated with a `FieldNode`. */ export class FieldMetadataState { - /** A map of all `MetadataKey` and `AggregateMetadataKey` that have been defined for this field. */ - private readonly metadata = new Map< - MetadataKey | AggregateMetadataKey, - unknown - >(); + /** A map of all `MetadataKey` that have been defined for this field. */ + private readonly metadata = new Map, unknown>(); constructor(private readonly node: FieldNode) { - // Field nodes (and thus their metadata state) are created in a linkedSignal in order to mirror - // the structure of the model data. We need to run the metadata factories untracked so that they - // do not cause recomputation of the linkedSignal. - untracked(() => - // Metadata factories are run in the form's injection context so they can create resources - // and inject DI dependencies. - runInInjectionContext(this.node.structure.injector, () => { - for (const [key, factory] of this.node.logicNode.logic.getMetadataFactoryEntries()) { - this.metadata.set(key, factory(this.node.context)); - } - }), - ); + // Force eager creation of managed keys, + // as managed keys have a `create` function that needs to run during construction. + for (const key of this.node.logicNode.logic.getMetadataKeys()) { + if (key.create) { + const logic = this.node.logicNode.logic.getMetadata(key); + const result = untracked(() => + runInInjectionContext(this.node.structure.injector, () => + key.create!(computed(() => logic.compute(this.node.context))), + ), + ); + this.metadata.set(key, result); + } + } } - /** Gets the value of a `MetadataKey` or `AggregateMetadataKey` for the field. */ - get(key: MetadataKey | AggregateMetadataKey): T | undefined | Signal { - if (key instanceof MetadataKey) { - return this.metadata.get(key) as T | undefined; - } - // Aggregate metadata comes with an initial value, and are considered to exist for every field. - // If no logic explicitly contributes values for the metadata, it is just considered to be the - // initial value. Therefore if the user asks for aggregate metadata for a field, - // we just create its computed on the fly. - cast>(key); - if (!this.metadata.has(key)) { - const logic = this.node.logicNode.logic.getAggregateMetadata(key); - const result = computed(() => logic.compute(this.node.context)); - this.metadata.set(key, result); + /** Gets the value of an `MetadataKey` for the field. */ + get(key: MetadataKey): T | undefined { + // We create non-managed metadata lazily, the first time they are accessed. + if (this.has(key)) { + if (!this.metadata.has(key)) { + if (key.create) { + throw Error('Managed metadata cannot be created lazily'); + } + const logic = this.node.logicNode.logic.getMetadata(key); + this.metadata.set( + key, + computed(() => logic.compute(this.node.context)), + ); + } } - return this.metadata.get(key)! as Signal; + return this.metadata.get(key) as T | undefined; } /** Checks whether the current metadata state has the given metadata key. */ - has(key: MetadataKey | AggregateMetadataKey): boolean { - if (key instanceof AggregateMetadataKey) { - // For aggregate metadata keys, they get added to the map lazily, on first access, so we can't - // rely on checking presence in the metadata map. Instead we check if there is any logic for - // the given metadata key. - return this.node.logicNode.logic.hasAggregateMetadata(key); - } else { - // Non-aggregate metadata gets added to our metadata map on construction, so we can just - // refer to their presence in the map. - return this.metadata.has(key); - } + has(key: MetadataKey): boolean { + // Metadata keys get added to the map lazily, on first access, + // so we can't rely on checking presence in the metadata map. + // Instead we check if there is any logic for the given metadata key. + return this.node.logicNode.logic.hasMetadata(key); } } diff --git a/packages/forms/signals/src/field/node.ts b/packages/forms/signals/src/field/node.ts index ddf8f0534abf..5b236c109c58 100644 --- a/packages/forms/signals/src/field/node.ts +++ b/packages/forms/signals/src/field/node.ts @@ -9,17 +9,17 @@ import {computed, linkedSignal, type Signal, untracked, type WritableSignal} from '@angular/core'; import type {Field} from '../api/field_directive'; import { - AggregateMetadataKey, MAX, MAX_LENGTH, - MetadataKey, + type MetadataKey, MIN, MIN_LENGTH, PATTERN, REQUIRED, -} from '../api/metadata'; +} from '../api/rules/metadata'; +import type {ValidationError} from '../api/rules/validation/validation_errors'; import type {DisabledReason, FieldContext, FieldState, FieldTree} from '../api/types'; -import type {ValidationError} from '../api/validation_errors'; +import {DYNAMIC} from '../schema/logic'; import {LogicNode} from '../schema/logic_node'; import {FieldPathNode} from '../schema/path_node'; import {FieldNodeContext} from './context'; @@ -29,11 +29,11 @@ import {FieldMetadataState} from './metadata'; import {FIELD_PROXY_HANDLER} from './proxy'; import {FieldNodeState} from './state'; import { - type ChildFieldNodeOptions, ChildFieldNodeStructure, type FieldNodeOptions, type FieldNodeStructure, RootFieldNodeStructure, + type TrackingKey, } from './structure'; import {FieldSubmitState} from './submit'; import {ValidationState} from './validation'; @@ -68,8 +68,10 @@ export class FieldNode implements FieldState { * Proxy to this node which allows navigation of the form graph below it. */ readonly fieldProxy = new Proxy(() => this, FIELD_PROXY_HANDLER) as unknown as FieldTree; + private readonly pathNode: FieldPathNode; constructor(options: FieldNodeOptions) { + this.pathNode = options.pathNode; this.fieldAdapter = options.fieldAdapter; this.structure = this.fieldAdapter.createStructure(this, options); this.validationState = this.fieldAdapter.createValidationState(this, options); @@ -166,40 +168,35 @@ export class FieldNode implements FieldState { return this.nodeState.name; } - private metadataOrUndefined(key: AggregateMetadataKey): Signal | undefined { - return this.hasMetadata(key) ? this.metadata(key) : undefined; - } - get max(): Signal | undefined { - return this.metadataOrUndefined(MAX); + return this.metadata(MAX); } get maxLength(): Signal | undefined { - return this.metadataOrUndefined(MAX_LENGTH); + return this.metadata(MAX_LENGTH); } get min(): Signal | undefined { - return this.metadataOrUndefined(MIN); + return this.metadata(MIN); } get minLength(): Signal | undefined { - return this.metadataOrUndefined(MIN_LENGTH); + return this.metadata(MIN_LENGTH); } get pattern(): Signal { - return this.metadataOrUndefined(PATTERN) ?? EMPTY; + return this.metadata(PATTERN) ?? EMPTY; } get required(): Signal { - return this.metadataOrUndefined(REQUIRED) ?? FALSE; + return this.metadata(REQUIRED) ?? FALSE; } - metadata(key: AggregateMetadataKey): Signal; - metadata(key: MetadataKey): M | undefined; - metadata(key: MetadataKey | AggregateMetadataKey): Signal | M | undefined { + metadata(key: MetadataKey): M | undefined { return this.metadataState.get(key); } - hasMetadata(key: MetadataKey | AggregateMetadataKey): boolean { + + hasMetadata(key: MetadataKey): boolean { return this.metadataState.has(key); } @@ -231,7 +228,7 @@ export class FieldNode implements FieldState { } private _reset(value?: unknown) { - if (value) { + if (value !== undefined) { this.value.set(value); } @@ -299,35 +296,50 @@ export class FieldNode implements FieldState { return adapter.newRoot(fieldManager, value, pathNode, adapter); } - /** - * Creates a child field node based on the given options. - */ - private static newChild(options: ChildFieldNodeOptions): FieldNode { - return options.fieldAdapter.newChild(options); - } - createStructure(options: FieldNodeOptions) { return options.kind === 'root' ? new RootFieldNodeStructure( this, - options.pathNode, options.logic, options.fieldManager, options.value, - options.fieldAdapter, - FieldNode.newChild, + this.newChild.bind(this), ) : new ChildFieldNodeStructure( this, - options.pathNode, options.logic, options.parent, options.identityInParent, options.initialKeyInParent, - options.fieldAdapter, - FieldNode.newChild, + this.newChild.bind(this), ); } + + private newChild(key: string, trackingId: TrackingKey | undefined, isArray: boolean): FieldNode { + // Determine the logic for the field that we're defining. + let childPath: FieldPathNode | undefined; + let childLogic: LogicNode; + if (isArray) { + // Fields for array elements have their logic defined by the `element` mechanism. + // TODO: other dynamic data + childPath = this.pathNode.getChild(DYNAMIC); + childLogic = this.structure.logic.getChild(DYNAMIC); + } else { + // Fields for plain properties exist in our logic node's child map. + childPath = this.pathNode.getChild(key); + childLogic = this.structure.logic.getChild(key); + } + + return this.fieldAdapter.newChild({ + kind: 'child', + parent: this as ParentFieldNode, + pathNode: childPath, + logic: childLogic, + initialKeyInParent: key, + identityInParent: trackingId, + fieldAdapter: this.fieldAdapter, + }); + } } const EMPTY = computed(() => []); diff --git a/packages/forms/signals/src/field/proxy.ts b/packages/forms/signals/src/field/proxy.ts index 531c75248137..0327750b147f 100644 --- a/packages/forms/signals/src/field/proxy.ts +++ b/packages/forms/signals/src/field/proxy.ts @@ -41,7 +41,16 @@ export const FIELD_PROXY_HANDLER: ProxyHandler<() => FieldNode> = { // Allow access to the iterator. This allows the user to spread the field array into a // standard array in order to call methods like `filter`, `map`, etc. if (p === Symbol.iterator) { - return Array.prototype[p as any]; + return () => { + // When creating an iterator, we need to account for reactivity. The iterator itself will + // read things each time `.next()` is called, but that may happen outside of the context + // where the iterator was created (e.g. with `@for`, actual diffing happens outside the + // reactive context of the template). + // + // Instead, side-effectfully read the value here to ensure iterator creation is reactive. + tgt.value(); + return Array.prototype[Symbol.iterator].apply(tgt.fieldProxy); + }; } // Note: We can consider supporting additional array methods if we want in the future, // but they should be thoroughly tested. Just forwarding the method directly from the diff --git a/packages/forms/signals/src/field/state.ts b/packages/forms/signals/src/field/state.ts index 99987ed62a4b..a4192bb90d87 100644 --- a/packages/forms/signals/src/field/state.ts +++ b/packages/forms/signals/src/field/state.ts @@ -11,7 +11,7 @@ import type {Field} from '../api/field_directive'; import type {Debouncer, DisabledReason} from '../api/types'; import {DEBOUNCER} from './debounce'; import type {FieldNode} from './node'; -import {reduceChildren, shortCircuitTrue} from './util'; +import {shortCircuitTrue} from './util'; /** * The non-validation and non-submit state associated with a `FieldNode`, such as touched and dirty @@ -76,8 +76,7 @@ export class FieldNodeState { */ readonly dirty: Signal = computed(() => { const selfDirtyValue = this.selfDirty() && !this.isNonInteractive(); - return reduceChildren( - this.node, + return this.node.structure.reduceChildren( selfDirtyValue, (child, value) => value || child.nodeState.dirty(), shortCircuitTrue, @@ -93,8 +92,7 @@ export class FieldNodeState { */ readonly touched: Signal = computed(() => { const selfTouchedValue = this.selfTouched() && !this.isNonInteractive(); - return reduceChildren( - this.node, + return this.node.structure.reduceChildren( selfTouchedValue, (child, value) => value || child.nodeState.touched(), shortCircuitTrue, @@ -163,8 +161,8 @@ export class FieldNodeState { */ readonly debouncer: Signal<((signal: AbortSignal) => Promise | void) | undefined> = computed(() => { - if (this.node.logicNode.logic.hasAggregateMetadata(DEBOUNCER)) { - const debouncerLogic = this.node.logicNode.logic.getAggregateMetadata(DEBOUNCER); + if (this.node.logicNode.logic.hasMetadata(DEBOUNCER)) { + const debouncerLogic = this.node.logicNode.logic.getMetadata(DEBOUNCER); const debouncer = debouncerLogic.compute(this.node.context); // Even if this field has a `debounce()` rule, it could be applied conditionally and currently diff --git a/packages/forms/signals/src/field/structure.ts b/packages/forms/signals/src/field/structure.ts index b44bf92e681a..c683fcaf5395 100644 --- a/packages/forms/signals/src/field/structure.ts +++ b/packages/forms/signals/src/field/structure.ts @@ -12,10 +12,13 @@ import { Injector, linkedSignal, Signal, + untracked, WritableSignal, + ɵRuntimeError as RuntimeError, } from '@angular/core'; -import {DYNAMIC} from '../schema/logic'; +import {SignalFormsErrorCode} from '../errors'; + import {LogicNode} from '../schema/logic_node'; import type {FieldPathNode} from '../schema/path_node'; import {deepSignal} from '../util/deep_signal'; @@ -31,11 +34,21 @@ import type {FieldNode, ParentFieldNode} from './node'; * tracking key allocated for the object. */ export type TrackingKey = PropertyKey & {__brand: 'FieldIdentity'}; +export type ChildNodeCtor = ( + key: string, + trackingKey: TrackingKey | undefined, + isArray: boolean, +) => FieldNode; /** Structural component of a `FieldNode` which tracks its path, parent, and children. */ export abstract class FieldNodeStructure { - /** Computed map of child fields, based on the current value of this field. */ - abstract readonly childrenMap: Signal | undefined>; + /** + * Computed map of child fields, based on the current value of this field. + * + * This structure reacts to `this.value` and produces a new `ChildrenData` when the + * value changes structurally (fields added/removed/moved). + */ + protected abstract readonly childrenMap: Signal; /** The field's value. */ abstract readonly value: WritableSignal; @@ -58,6 +71,11 @@ export abstract class FieldNodeStructure { /** The parent field of this field. */ abstract readonly parent: FieldNode | undefined; + readonly logic: LogicNode; + readonly node: FieldNode; + + readonly createChildNode: ChildNodeCtor; + /** Added to array elements for tracking purposes. */ // TODO: given that we don't ever let a field move between parents, is it safe to just extract // this to a shared symbol for all fields, rather than having a separate one per parent? @@ -75,40 +93,280 @@ export abstract class FieldNodeStructure { return this._injector; } - constructor( - /** The logic to apply to this field. */ - readonly logic: LogicNode, - ) {} + constructor(logic: LogicNode, node: FieldNode, createChildNode: ChildNodeCtor) { + this.logic = logic; + this.node = node; + this.createChildNode = createChildNode; + } /** Gets the child fields of this field. */ children(): Iterable { - return this.childrenMap()?.values() ?? []; + const map = this.childrenMap(); + if (map === undefined) { + return []; + } + return Array.from(map.byPropertyKey.values()).map((child) => untracked(child.reader)!); } /** Retrieve a child `FieldNode` of this node by property key. */ getChild(key: PropertyKey): FieldNode | undefined { + const strKey = key.toString(); + + // Lookup the computed reader for this key in `childrenMap`. This lookup doesn't need to be + // reactive since `childrenMap` guarantees it will always return the same `reader` for the same + // `key`, so long as that key exists. + let reader = untracked(this.childrenMap)?.byPropertyKey.get(strKey)?.reader; + + if (!reader) { + // The key doesn't exist / doesn't have a child field associated with it. In this case, we + // need to be clever. We want to return `undefined`, but also be notified by reactivity if the + // field _does_ pop into existence later. Basically, we want to depend on a reader for a key + // that doesn't exist. + // + // We do precisely that by creating an ephemeral reader which will be read and then dropped. + // If we're in a reactive context, the ephemeral reader will live on in the dependencies of + // that context and notify it if the field is later created. When the reactive context reruns, + // it will again attempt the read which will call `getChild()`, which will then find the real + // reader for that key. + reader = this.createReader(strKey); + } + + return reader(); + } + + /** + * Perform a reduction over a field's children (if any) and return the result. + * + * Optionally, the reduction is short circuited based on the provided `shortCircuit` function. + */ + reduceChildren( + initialValue: T, + fn: (child: FieldNode, value: T) => T, + shortCircuit?: (value: T) => boolean, + ): T { const map = this.childrenMap(); - const value = this.value(); - if (!map || !isObject(value)) { - return undefined; + if (!map) { + return initialValue; } - if (isArray(value)) { - const childValue = value[key]; - if (isObject(childValue) && childValue.hasOwnProperty(this.identitySymbol)) { - // For arrays, we want to use the tracking identity of the value instead of the raw property - // as our index into the `childrenMap`. - key = childValue[this.identitySymbol] as PropertyKey; + let value = initialValue; + for (const child of map.byPropertyKey.values()) { + if (shortCircuit?.(value)) { + break; } + value = fn(untracked(child.reader)!, value); } - - return map.get((typeof key === 'number' ? key.toString() : key) as TrackingKey); + return value; } /** Destroys the field when it is no longer needed. */ destroy(): void { this.injector.destroy(); } + + /** + * Creates a keyInParent signal for a field node. + * + * For root nodes, returns ROOT_KEY_IN_PARENT which throws when accessed. + * For child nodes, creates a computed that tracks the field's current key in its parent, + * with special handling for tracked array elements. + * + * @param options The field node options + * @param identityInParent The tracking identity (only for tracked array children) + * @param initialKeyInParent The initial key in parent (only for child nodes) + * @returns A signal representing the field's key in its parent + */ + protected createKeyInParent( + options: FieldNodeOptions, + identityInParent: TrackingKey | undefined, + initialKeyInParent: string | undefined, + ): Signal { + if (options.kind === 'root') { + return ROOT_KEY_IN_PARENT; + } + + if (identityInParent === undefined) { + const key = initialKeyInParent!; + return computed(() => { + if (this.parent!.structure.getChild(key) !== this.node) { + throw new RuntimeError( + SignalFormsErrorCode.ORPHAN_FIELD_PROPERTY, + ngDevMode && + `Orphan field, looking for property '${key}' of ${getDebugName(this.parent!)}`, + ); + } + return key; + }); + } else { + let lastKnownKey = initialKeyInParent!; + return computed(() => { + // TODO(alxhub): future perf optimization: here we depend on the parent's value, but most + // changes to the value aren't structural - they aren't moving around objects and thus + // shouldn't affect `keyInParent`. We currently mitigate this issue via `lastKnownKey` + // which avoids a search. + const parentValue = this.parent!.structure.value(); + if (!isArray(parentValue)) { + // It should not be possible to encounter this error. It would require the parent to + // change from an array field to non-array field. However, in the current implementation + // a field's parent can never change. + throw new RuntimeError( + SignalFormsErrorCode.ORPHAN_FIELD_ARRAY, + ngDevMode && `Orphan field, expected ${getDebugName(this.parent!)} to be an array`, + ); + } + + // Check the parent value at the last known key to avoid a scan. + // Note: lastKnownKey is a string, but we pretend to typescript like its a number, + // since accessing someArray['1'] is the same as accessing someArray[1] + const data = parentValue[lastKnownKey as unknown as number]; + if ( + isObject(data) && + data.hasOwnProperty(this.parent!.structure.identitySymbol) && + data[this.parent!.structure.identitySymbol] === identityInParent + ) { + return lastKnownKey; + } + + // Otherwise, we need to check all the keys in the parent. + for (let i = 0; i < parentValue.length; i++) { + const data = parentValue[i]; + if ( + isObject(data) && + data.hasOwnProperty(this.parent!.structure.identitySymbol) && + data[this.parent!.structure.identitySymbol] === identityInParent + ) { + return (lastKnownKey = i.toString()); + } + } + + throw new RuntimeError( + SignalFormsErrorCode.ORPHAN_FIELD_NOT_FOUND, + ngDevMode && `Orphan field, can't find element in array ${getDebugName(this.parent!)}`, + ); + }); + } + } + + protected createChildrenMap(): Signal { + return linkedSignal({ + source: this.value, + computation: ( + value: unknown, + previous: {source: unknown; value: ChildrenData | undefined} | undefined, + ): ChildrenData | undefined => { + if (!isObject(value)) { + // Non-object values have no children. This short-circuit path makes `childrenMap` fast + // for primitive-valued fields. + return undefined; + } + + // Previous `ChildrenData` (immutable). This is also where we first initialize our map if + // needed. + const prevData: ChildrenData = previous?.value ?? { + byPropertyKey: new Map(), + }; + + // The next `ChildrenData` object to be returned. Initialized lazily when we know there's + // been a structural change to the model. + let data: MutableChildrenData | undefined; + + const parentIsArray = isArray(value); + + // Remove fields that have disappeared since the last time this map was computed. + if (prevData !== undefined) { + if (parentIsArray) { + data = maybeRemoveStaleArrayFields(prevData, value, this.identitySymbol); + } else { + data = maybeRemoveStaleObjectFields(prevData, value); + } + } + + // Now, go through the values and add any new ones. + for (const key of Object.keys(value)) { + let trackingKey: TrackingKey | undefined = undefined; + const childValue = value[key] as unknown; + + // Fields explicitly set to `undefined` are treated as if they don't exist. + // This ensures that `{value: undefined}` and `{}` have the same behavior for their `value` + // field. + if (childValue === undefined) { + // The value might have _become_ `undefined`, so we need to delete it here. + if (prevData.byPropertyKey.has(key)) { + data ??= {...(prevData as MutableChildrenData)}; + data.byPropertyKey.delete(key); + } + continue; + } + + if (parentIsArray && isObject(childValue) && !isArray(childValue)) { + // For object values in arrays, assign a synthetic identity. This will be used to + // preserve the field instance even as this object moves around in the parent array. + trackingKey = (childValue[this.identitySymbol] as TrackingKey) ??= Symbol( + ngDevMode ? `id:${globalId++}` : '', + ) as TrackingKey; + } + + let childNode: FieldNode | undefined; + + if (trackingKey) { + // If tracking is in use, then the `FieldNode` instance is always managed via its + // tracking key. Create the instance if needed, or look it up otherwise. + if (!prevData.byTrackingKey?.has(trackingKey)) { + data ??= {...(prevData as MutableChildrenData)}; + data.byTrackingKey ??= new Map(); + + data.byTrackingKey.set( + trackingKey, + this.createChildNode(key, trackingKey, parentIsArray), + ); + } + + // Note: data ?? prevData is needed because we might have freshly instantiated + // `byTrackingKey` only in `data` above. + childNode = (data ?? prevData).byTrackingKey!.get(trackingKey)!; + } + + // Next, make sure the `ChildData` for this key in `byPropertyKey` is up to date. We need + // to consider two cases: + // + // 1. No record exists for this field (yet). + // 2. A record does exist, but the field identity at this key has changed (only possible + // when fields are tracked). + const child = prevData.byPropertyKey.get(key); + if (child === undefined) { + // No record exists yet - create one. + data ??= {...(prevData as MutableChildrenData)}; + + data.byPropertyKey.set(key, { + // TODO: creating a computed per-key is overkill when the field at a key can't change + // (e.g. the value is not an array). Maybe this can be optimized? + reader: this.createReader(key), + // If tracking is in use, then it already created/found the `childNode` for this key. + // Otherwise we create the child field here. + node: childNode ?? this.createChildNode(key, trackingKey, parentIsArray), + }); + } else if (childNode && childNode !== child.node) { + // A record exists, but records the wrong `FieldNode`. Update it. + data ??= {...(prevData as MutableChildrenData)}; + child.node = childNode; + } + } + + return data ?? prevData; + }, + }); + } + + /** + * Creates a "reader" computed for the given key. + * + * A reader is a computed signal that memoizes the access of the `FieldNode` stored at this key + * (or returns `undefined` if no such field exists). Accessing fields via the reader ensures that + * reactive consumers aren't notified unless the field at a key actually changes. + */ + private createReader(key: string): Signal { + return computed(() => this.childrenMap()?.byPropertyKey.get(key)?.node); + } } /** The structural component of a `FieldNode` that is the root of its field tree. */ @@ -129,7 +387,7 @@ export class RootFieldNodeStructure extends FieldNodeStructure { return ROOT_KEY_IN_PARENT; } - override readonly childrenMap: Signal | undefined>; + protected override readonly childrenMap: Signal; /** * Creates the structure for the root node of a field tree. @@ -144,24 +402,14 @@ export class RootFieldNodeStructure extends FieldNodeStructure { */ constructor( /** The full field node that corresponds to this structure. */ - private readonly node: FieldNode, - pathNode: FieldPathNode, + node: FieldNode, logic: LogicNode, override readonly fieldManager: FormFieldManager, override readonly value: WritableSignal, - adapter: FieldAdapter, - createChildNode: (options: ChildFieldNodeOptions) => FieldNode, + createChildNode: ChildNodeCtor, ) { - super(logic); - this.childrenMap = makeChildrenMapSignal( - node as ParentFieldNode, - value, - this.identitySymbol, - pathNode, - logic, - adapter, - createChildNode, - ); + super(logic, node, createChildNode); + this.childrenMap = this.createChildrenMap(); } } @@ -171,8 +419,7 @@ export class ChildFieldNodeStructure extends FieldNodeStructure { override readonly pathKeys: Signal; override readonly keyInParent: Signal; override readonly value: WritableSignal; - - override readonly childrenMap: Signal | undefined>; + override readonly childrenMap: Signal; override get fieldManager(): FormFieldManager { return this.root.structure.fieldManager; @@ -192,88 +439,34 @@ export class ChildFieldNodeStructure extends FieldNodeStructure { */ constructor( node: FieldNode, - pathNode: FieldPathNode, - logic: LogicNode, + override readonly logic: LogicNode, override readonly parent: ParentFieldNode, identityInParent: TrackingKey | undefined, initialKeyInParent: string, - adapter: FieldAdapter, - createChildNode: (options: ChildFieldNodeOptions) => FieldNode, + createChildNode: ChildNodeCtor, ) { - super(logic); + super(logic, node, createChildNode); this.root = this.parent.structure.root; - this.pathKeys = computed(() => [...parent.structure.pathKeys(), this.keyInParent()]); - - if (identityInParent === undefined) { - const key = initialKeyInParent; - this.keyInParent = computed(() => { - if (parent.structure.childrenMap()?.get(key as TrackingKey) !== node) { - throw new Error( - `RuntimeError: orphan field, looking for property '${key}' of ${getDebugName(parent)}`, - ); - } - return key; - }); - } else { - let lastKnownKey = initialKeyInParent; - this.keyInParent = computed(() => { - // TODO(alxhub): future perf optimization: here we depend on the parent's value, but most - // changes to the value aren't structural - they aren't moving around objects and thus - // shouldn't affect `keyInParent`. We currently mitigate this issue via `lastKnownKey` - // which avoids a search. - const parentValue = parent.structure.value(); - if (!isArray(parentValue)) { - // It should not be possible to encounter this error. It would require the parent to - // change from an array field to non-array field. However, in the current implementation - // a field's parent can never change. - throw new Error( - `RuntimeError: orphan field, expected ${getDebugName(parent)} to be an array`, - ); - } - - // Check the parent value at the last known key to avoid a scan. - // Note: lastKnownKey is a string, but we pretend to typescript like its a number, - // since accessing someArray['1'] is the same as accessing someArray[1] - const data = parentValue[lastKnownKey as unknown as number]; - if ( - isObject(data) && - data.hasOwnProperty(parent.structure.identitySymbol) && - data[parent.structure.identitySymbol] === identityInParent - ) { - return lastKnownKey; - } - - // Otherwise, we need to check all the keys in the parent. - for (let i = 0; i < parentValue.length; i++) { - const data = parentValue[i]; - if ( - isObject(data) && - data.hasOwnProperty(parent.structure.identitySymbol) && - data[parent.structure.identitySymbol] === identityInParent - ) { - return (lastKnownKey = i.toString()); - } - } + this.keyInParent = this.createKeyInParent( + { + kind: 'child', + parent, + pathNode: undefined!, + logic, + initialKeyInParent, + identityInParent, + fieldAdapter: undefined!, + }, + identityInParent, + initialKeyInParent, + ); - throw new Error( - `RuntimeError: orphan field, can't find element in array ${getDebugName(parent)}`, - ); - }); - } + this.pathKeys = computed(() => [...parent.structure.pathKeys(), this.keyInParent()]); this.value = deepSignal(this.parent.structure.value, this.keyInParent); - this.childrenMap = makeChildrenMapSignal( - node as ParentFieldNode, - this.value, - this.identitySymbol, - pathNode, - logic, - adapter, - createChildNode, - ); - + this.childrenMap = this.createChildrenMap(); this.fieldManager.structures.add(this); } } @@ -326,135 +519,111 @@ const ROOT_PATH_KEYS = computed(() => []); * do not have a parent. This signal will throw if it is read. */ const ROOT_KEY_IN_PARENT = computed(() => { - throw new Error(`RuntimeError: the top-level field in the form has no parent`); + throw new RuntimeError( + SignalFormsErrorCode.ROOT_FIELD_NO_PARENT, + ngDevMode && 'The top-level field in the form has no parent.', + ); }); +/** Gets a human readable name for a field node for use in error messages. */ +function getDebugName(node: FieldNode) { + return `.${node.structure.pathKeys().join('.')}`; +} + +interface MutableChildrenData { + readonly byPropertyKey: Map; + byTrackingKey?: Map; +} + /** - * Creates a linked signal map of all child fields for a field. - * - * @param node The field to create the children map signal for. - * @param valueSignal The value signal for the field. - * @param identitySymbol The key used to access the tracking id of a field. - * @param pathNode The path node corresponding to the field in the schema. - * @param logic The logic to apply to the field. - * @param adapter Adapter that knows how to create new fields and appropriate state. - * @param createChildNode A factory function to create child nodes for this field. - * @returns + * Derived data regarding child fields for a specific parent field. */ -function makeChildrenMapSignal( - node: FieldNode, - valueSignal: WritableSignal, - identitySymbol: symbol, - pathNode: FieldPathNode, - logic: LogicNode, - adapter: FieldAdapter, - createChildNode: (options: ChildFieldNodeOptions) => FieldNode, -): Signal | undefined> { - // We use a `linkedSignal` to preserve the instances of `FieldNode` for each child field even if - // the value of this field changes its object identity. The computation creates or updates the map - // of child `FieldNode`s for `node` based on its current value. - return linkedSignal | undefined>({ - source: valueSignal, - computation: (value, previous): Map | undefined => { - // We may or may not have a previous map. If there isn't one, then `childrenMap` will be lazily - // initialized to a new map instance if needed. - let childrenMap = previous?.value; - - if (!isObject(value)) { - // Non-object values have no children. - return undefined; - } - const isValueArray = isArray(value); - - // Remove fields that have disappeared since the last time this map was computed. - if (childrenMap !== undefined) { - let oldKeys: Set | undefined = undefined; - if (isValueArray) { - oldKeys = new Set(childrenMap.keys()); - for (let i = 0; i < value.length; i++) { - const childValue = value[i] as unknown; - if (isObject(childValue) && childValue.hasOwnProperty(identitySymbol)) { - oldKeys.delete(childValue[identitySymbol] as TrackingKey); - } else { - oldKeys.delete(i.toString() as TrackingKey); - } - } - - for (const key of oldKeys) { - childrenMap.delete(key); - } - } else { - for (let key of childrenMap.keys()) { - if (!value.hasOwnProperty(key)) { - childrenMap.delete(key); - } - } - } - } +interface ChildrenData { + /** + * Tracks `ChildData` for each property key within the parent. + */ + readonly byPropertyKey: ReadonlyMap; - // Add fields that exist in the value but don't yet have instances in the map. - for (let key of Object.keys(value)) { - let trackingId: TrackingKey | undefined = undefined; - const childValue = value[key] as unknown; - - // Fields explicitly set to `undefined` are treated as if they don't exist. - // This ensures that `{value: undefined}` and `{}` have the same behavior for their `value` - // field. - if (childValue === undefined) { - // The value might have _become_ `undefined`, so we need to delete it here. - childrenMap?.delete(key as TrackingKey); - continue; - } + /** + * Tracks the instance of child `FieldNode`s by their tracking key, which is always 1:1 with the + * fields, even if they move around in the parent. + */ + readonly byTrackingKey?: ReadonlyMap; +} - if (isValueArray && isObject(childValue) && !isArray(childValue)) { - // For object values in arrays, assign a synthetic identity instead. - trackingId = (childValue[identitySymbol] as TrackingKey) ??= Symbol( - ngDevMode ? `id:${globalId++}` : '', - ) as TrackingKey; - } +/** + * Data for a specific child within a parent. + */ +interface ChildData { + /** + * A computed signal to access the `FieldNode` currently stored at a specific key. + * + * Because this is a computed, it only updates whenever the `FieldNode` at that key changes. + * Because `ChildData` is always associated with a specific key via `ChildrenData.byPropertyKey`, + * this computed gives a stable way to watch the field stored for a given property and only + * receives notifications when that field changes. + */ + readonly reader: Signal; - const identity = trackingId ?? (key as TrackingKey); + /** + * The child `FieldNode` currently stored at this key. + */ + node: FieldNode; +} - if (childrenMap?.has(identity)) { - continue; - } +function maybeRemoveStaleArrayFields( + prevData: ChildrenData, + value: ReadonlyArray, + identitySymbol: PropertyKey, +): MutableChildrenData | undefined { + let data: MutableChildrenData | undefined; + + // TODO: we should be able to optimize this diff away in the fast case where nothing has + // actually changed structurally. + const oldKeys = new Set(prevData.byPropertyKey.keys()); + const oldTracking = new Set(prevData.byTrackingKey?.keys()); + + for (let i = 0; i < value.length; i++) { + const childValue = value[i]; + oldKeys.delete(i.toString()); + if (isObject(childValue) && childValue.hasOwnProperty(identitySymbol)) { + oldTracking.delete(childValue[identitySymbol] as TrackingKey); + } + } - // Determine the logic for the field that we're defining. - let childPath: FieldPathNode | undefined; - let childLogic: LogicNode; - if (isValueArray) { - // Fields for array elements have their logic defined by the `element` mechanism. - // TODO: other dynamic data - childPath = pathNode.getChild(DYNAMIC); - childLogic = logic.getChild(DYNAMIC); - } else { - // Fields for plain properties exist in our logic node's child map. - childPath = pathNode.getChild(key); - childLogic = logic.getChild(key); - } + // `oldKeys` and `oldTracking` now contain stale keys and tracking keys, respectively. + // Remove them from their corresponding maps. - childrenMap ??= new Map(); - childrenMap.set( - identity, - createChildNode({ - kind: 'child', - parent: node as ParentFieldNode, - pathNode: childPath, - logic: childLogic, - initialKeyInParent: key, - identityInParent: trackingId, - fieldAdapter: adapter, - }), - ); - } + if (oldKeys.size > 0) { + data ??= {...(prevData as MutableChildrenData)}; + for (const key of oldKeys) { + data.byPropertyKey.delete(key); + } + } + if (oldTracking.size > 0) { + data ??= {...(prevData as MutableChildrenData)}; + for (const id of oldTracking) { + data.byTrackingKey?.delete(id); + } + } - return childrenMap; - }, - equal: () => false, - }); + return data; } -/** Gets a human readable name for a field node for use in error messages. */ -function getDebugName(node: FieldNode) { - return `.${node.structure.pathKeys().join('.')}`; +function maybeRemoveStaleObjectFields( + prevData: ChildrenData, + value: Record, +): MutableChildrenData | undefined { + let data: MutableChildrenData | undefined; + + // For objects, we diff a bit differently, and use the value to check whether an old + // property still exists on the object value. + for (const key of prevData.byPropertyKey.keys()) { + if (!value.hasOwnProperty(key)) { + data ??= {...(prevData as MutableChildrenData)}; + data.byPropertyKey.delete(key); + } + } + + return data; } diff --git a/packages/forms/signals/src/field/submit.ts b/packages/forms/signals/src/field/submit.ts index 9c87ec438f00..bbe7a5153810 100644 --- a/packages/forms/signals/src/field/submit.ts +++ b/packages/forms/signals/src/field/submit.ts @@ -7,7 +7,7 @@ */ import {computed, linkedSignal, Signal, signal, WritableSignal} from '@angular/core'; -import {ValidationError} from '../api/validation_errors'; +import {ValidationError} from '../api/rules/validation/validation_errors'; import type {FieldNode} from './node'; /** @@ -20,11 +20,11 @@ export class FieldSubmitState { */ readonly selfSubmitting = signal(false); - /** Server errors that are associated with this field. */ - readonly serverErrors: WritableSignal; + /** Submission errors that are associated with this field. */ + readonly submissionErrors: WritableSignal; constructor(private readonly node: FieldNode) { - this.serverErrors = linkedSignal({ + this.submissionErrors = linkedSignal({ source: this.node.structure.value, computation: () => [] as readonly ValidationError.WithField[], }); diff --git a/packages/forms/signals/src/field/util.ts b/packages/forms/signals/src/field/util.ts index 88676893ae8f..6c89cd8b0055 100644 --- a/packages/forms/signals/src/field/util.ts +++ b/packages/forms/signals/src/field/util.ts @@ -6,34 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ -import type {FieldNode} from './node'; import type {FieldNodeOptions} from './structure'; -/** - * Perform a reduction over a field's children (if any) and return the result. - * - * Optionally, the reduction is short circuited based on the provided `shortCircuit` function. - */ -export function reduceChildren( - node: FieldNode, - initialValue: T, - fn: (child: FieldNode, value: T) => T, - shortCircuit?: (value: T) => boolean, -): T { - const childrenMap = node.structure.childrenMap(); - if (!childrenMap) { - return initialValue; - } - let value = initialValue; - for (const child of childrenMap.values()) { - if (shortCircuit?.(value)) { - break; - } - value = fn(child, value); - } - return value; -} - /** A shortCircuit function for reduceChildren that short-circuits if the value is false. */ export function shortCircuitFalse(value: boolean): boolean { return !value; diff --git a/packages/forms/signals/src/field/validation.ts b/packages/forms/signals/src/field/validation.ts index 31ad4f778d87..936e471c6e12 100644 --- a/packages/forms/signals/src/field/validation.ts +++ b/packages/forms/signals/src/field/validation.ts @@ -7,11 +7,11 @@ */ import {computed, Signal, ɵWritable} from '@angular/core'; +import type {ValidationError} from '../api/rules/validation/validation_errors'; import type {FieldTree, TreeValidationResult, ValidationResult} from '../api/types'; -import type {ValidationError} from '../api/validation_errors'; import {isArray} from '../util/type_guards'; import type {FieldNode} from './node'; -import {reduceChildren, shortCircuitFalse} from './util'; +import {shortCircuitFalse} from './util'; /** * Helper function taking validation state, and returning own state of the node. @@ -38,8 +38,8 @@ export interface ValidationState { rawSyncTreeErrors: Signal; /** - * The full set of synchronous errors for this field, including synchronous tree errors and server - * errors. Server errors are considered "synchronous" because they are imperatively added. From + * The full set of synchronous errors for this field, including synchronous tree errors and submission + * errors. Submission errors are considered "synchronous" because they are imperatively added. From * the perspective of the field state they are either there or not, they are never in a pending * state. */ @@ -163,10 +163,10 @@ export class FieldValidationState implements ValidationState { }); /** - * The full set of synchronous errors for this field, including synchronous tree errors and server - * errors. Server errors are considered "synchronous" because they are imperatively added. From - * the perspective of the field state they are either there or not, they are never in a pending - * state. + * The full set of synchronous errors for this field, including synchronous tree errors and + * submission errors. Submission errors are considered "synchronous" because they are imperatively + * added. From the perspective of the field state they are either there or not, they are never in a + * pending state. */ readonly syncErrors: Signal = computed(() => { // Short-circuit running validators if validation doesn't apply to this field. @@ -177,7 +177,7 @@ export class FieldValidationState implements ValidationState { return [ ...this.node.logicNode.logic.syncErrors.compute(this.node.context), ...this.syncTreeErrors(), - ...normalizeErrors(this.node.submitState.serverErrors()), + ...normalizeErrors(this.node.submitState.submissionErrors()), ]; }); @@ -191,8 +191,7 @@ export class FieldValidationState implements ValidationState { return true; } - return reduceChildren( - this.node, + return this.node.structure.reduceChildren( this.syncErrors().length === 0, (child, value) => value && child.validationState.syncValid(), shortCircuitFalse, @@ -204,7 +203,7 @@ export class FieldValidationState implements ValidationState { * rather than a descendant. */ readonly syncTreeErrors: Signal = computed(() => - this.rawSyncTreeErrors().filter((err) => err.field === this.node.fieldProxy), + this.rawSyncTreeErrors().filter((err) => err.fieldTree === this.node.fieldProxy), ); /** @@ -236,7 +235,7 @@ export class FieldValidationState implements ValidationState { return []; } return this.rawAsyncErrors().filter( - (err) => err === 'pending' || err.field === this.node.fieldProxy, + (err) => err === 'pending' || err.fieldTree === this.node.fieldProxy, ); }); @@ -249,7 +248,7 @@ export class FieldValidationState implements ValidationState { ]); readonly errorSummary = computed(() => - reduceChildren(this.node, this.errors(), (child, result) => [ + this.node.structure.reduceChildren(this.errors(), (child, result) => [ ...result, ...child.errorSummary(), ]), @@ -259,8 +258,7 @@ export class FieldValidationState implements ValidationState { * Whether this field has any asynchronous validators still pending. */ readonly pending = computed(() => - reduceChildren( - this.node, + this.node.structure.reduceChildren( this.asyncErrors().includes('pending'), (child, value) => value || child.validationState.asyncErrors().includes('pending'), ), @@ -290,8 +288,7 @@ export class FieldValidationState implements ValidationState { } let ownStatus = calculateValidationSelfStatus(this); - return reduceChildren<'valid' | 'invalid' | 'unknown'>( - this.node, + return this.node.structure.reduceChildren<'valid' | 'invalid' | 'unknown'>( ownStatus, (child, value) => { if (value === 'invalid' || child.validationState.status() === 'invalid') { @@ -354,27 +351,27 @@ function normalizeErrors(error: T | readonly T[]): r /** * Sets the given field on the given error(s) if it does not already have a field. * @param error The error(s) to add the field to - * @param field The default field to add + * @param fieldTree The default field to add * @returns The passed in error(s), with its field set. */ export function addDefaultField( error: E, - field: FieldTree, -): E & {field: FieldTree}; + fieldTree: FieldTree, +): E & {fieldTree: FieldTree}; export function addDefaultField( errors: TreeValidationResult, - field: FieldTree, -): ValidationResult}>; + fieldTree: FieldTree, +): ValidationResult}>; export function addDefaultField( errors: TreeValidationResult, - field: FieldTree, -): ValidationResult}> { + fieldTree: FieldTree, +): ValidationResult}> { if (isArray(errors)) { for (const error of errors) { - (error as ɵWritable).field ??= field; + (error as ɵWritable).fieldTree ??= fieldTree; } } else if (errors) { - (errors as ɵWritable).field ??= field; + (errors as ɵWritable).fieldTree ??= fieldTree; } - return errors as ValidationResult}>; + return errors as ValidationResult}>; } diff --git a/packages/forms/signals/src/schema/logic.ts b/packages/forms/signals/src/schema/logic.ts index f00334687c1d..fd3c3d2421b7 100644 --- a/packages/forms/signals/src/schema/logic.ts +++ b/packages/forms/signals/src/schema/logic.ts @@ -7,10 +7,11 @@ */ import {untracked} from '@angular/core'; -import {AggregateMetadataKey, MetadataKey} from '../api/metadata'; -import {DisabledReason, type FieldContext, type SchemaPath, type LogicFn} from '../api/types'; -import type {ValidationError} from '../api/validation_errors'; +import type {MetadataKey} from '../api/rules/metadata'; +import type {ValidationError} from '../api/rules/validation/validation_errors'; +import {DisabledReason, type FieldContext, type LogicFn, type SchemaPath} from '../api/types'; import type {FieldNode} from '../field/node'; +import {cast} from '../field/util'; import {isArray} from '../util/type_guards'; /** @@ -176,28 +177,28 @@ export class ArrayMergeLogic extends ArrayMergeIgnoreLogic extends AbstractLogic { +/** Logic that combines metadata according to the keys's reduce function. */ +export class MetadataMergeLogic extends AbstractLogic { override get defaultValue() { - return this.key.getInitial(); + return this.key.reducer.getInitial(); } constructor( predicates: ReadonlyArray, - private key: AggregateMetadataKey, + private key: MetadataKey, ) { super(predicates); } override compute(ctx: FieldContext): TAcc { if (this.fns.length === 0) { - return this.key.getInitial(); + return this.key.reducer.getInitial(); } - let acc: TAcc = this.key.getInitial(); + let acc: TAcc = this.key.reducer.getInitial(); for (let i = 0; i < this.fns.length; i++) { const item = this.fns[i](ctx); if (item !== IGNORED) { - acc = this.key.reduce(acc, item); + acc = this.key.reducer.reduce(acc, item); } } return acc; @@ -258,16 +259,11 @@ export class LogicContainer { readonly syncTreeErrors: ArrayMergeIgnoreLogic; /** Logic that produces asynchronous validation results (errors or 'pending'). */ readonly asyncErrors: ArrayMergeIgnoreLogic; - /** A map of aggregate metadata keys to the `AbstractLogic` instances that compute their values. */ - private readonly aggregateMetadataKeys = new Map< - AggregateMetadataKey, + /** A map of metadata keys to the `AbstractLogic` instances that compute their values. */ + private readonly metadata = new Map< + MetadataKey, AbstractLogic >(); - /** A map of metadata keys to the factory functions that create their values. */ - private readonly metadataFactories = new Map< - MetadataKey, - (ctx: FieldContext) => unknown - >(); /** * Constructs a new `Logic` container. @@ -285,56 +281,30 @@ export class LogicContainer { ); } - /** Checks whether there is logic for the given aggregate metadata key. */ - hasAggregateMetadata(key: AggregateMetadataKey) { - return this.aggregateMetadataKeys.has(key); - } - - /** - * Gets an iterable of [aggregate metadata, logic function] pairs. - * @returns An iterable of aggregate metadata entries. - */ - getAggregateMetadataEntries() { - return this.aggregateMetadataKeys.entries(); + /** Checks whether there is logic for the given metadata key. */ + hasMetadata(key: MetadataKey) { + return this.metadata.has(key); } /** - * Gets an iterable of [metadata, value factory function] pairs. - * @returns An iterable of metadata factory entries. + * Gets an iterable of [metadata key, logic function] pairs. + * @returns An iterable of metadata keys. */ - getMetadataFactoryEntries() { - return this.metadataFactories.entries(); + getMetadataKeys() { + return this.metadata.keys(); } /** - * Retrieves or creates the `AbstractLogic` for a given aggregate metadata key. - * @param key The `AggregateMetadataKey` for which to get the logic. + * Retrieves or creates the `AbstractLogic` for a given metadata key. + * @param key The `MetadataKey` for which to get the logic. * @returns The `AbstractLogic` associated with the key. */ - getAggregateMetadata(key: AggregateMetadataKey): AbstractLogic { - if (!this.aggregateMetadataKeys.has(key as AggregateMetadataKey)) { - this.aggregateMetadataKeys.set( - key as AggregateMetadataKey, - new AggregateMetadataMergeLogic(this.predicates, key), - ); + getMetadata(key: MetadataKey): AbstractLogic { + cast>(key); + if (!this.metadata.has(key)) { + this.metadata.set(key, new MetadataMergeLogic(this.predicates, key)); } - return this.aggregateMetadataKeys.get( - key as AggregateMetadataKey, - )! as AbstractLogic; - } - - /** - * Adds a factory function for a given metadata key. - * @param key The `MetadataKey` to associate the factory with. - * @param factory The factory function. - * @throws If a factory is already defined for the given key. - */ - addMetadataFactory(key: MetadataKey, factory: (ctx: FieldContext) => unknown) { - if (this.metadataFactories.has(key)) { - // TODO: name of the metadata key? - throw new Error(`Can't define value twice for the same MetadataKey`); - } - this.metadataFactories.set(key, factory); + return this.metadata.get(key)! as AbstractLogic; } /** @@ -348,11 +318,9 @@ export class LogicContainer { this.syncErrors.mergeIn(other.syncErrors); this.syncTreeErrors.mergeIn(other.syncTreeErrors); this.asyncErrors.mergeIn(other.asyncErrors); - for (const [key, metadataLogic] of other.getAggregateMetadataEntries()) { - this.getAggregateMetadata(key).mergeIn(metadataLogic); - } - for (const [key, metadataFactory] of other.getMetadataFactoryEntries()) { - this.addMetadataFactory(key, metadataFactory); + for (const key of other.getMetadataKeys()) { + const metadataLogic = other.metadata.get(key)!; + this.getMetadata(key).mergeIn(metadataLogic); } } } diff --git a/packages/forms/signals/src/schema/logic_node.ts b/packages/forms/signals/src/schema/logic_node.ts index 23fb14af8ff4..b1d2d4767a02 100644 --- a/packages/forms/signals/src/schema/logic_node.ts +++ b/packages/forms/signals/src/schema/logic_node.ts @@ -6,17 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ -import {AggregateMetadataKey, MetadataKey} from '../api/metadata'; -import type { - AsyncValidationResult, - DisabledReason, - FieldContext, - LogicFn, - ValidationResult, -} from '../api/types'; -import type {ValidationError} from '../api/validation_errors'; +import {ɵRuntimeError as RuntimeError} from '@angular/core'; +import {SignalFormsErrorCode} from '../errors'; + +import type {ValidationError, MetadataKey} from '../api/rules'; +import type {AsyncValidationResult, DisabledReason, LogicFn, ValidationResult} from '../api/types'; import {setBoundPathDepthForResolution} from '../field/resolution'; -import {BoundPredicate, DYNAMIC, LogicContainer, Predicate} from './logic'; +import {type BoundPredicate, DYNAMIC, LogicContainer, type Predicate} from './logic'; /** * Abstract base class for building a `LogicNode`. @@ -48,14 +44,9 @@ export abstract class AbstractLogicNodeBuilder { /** Adds a rule for asynchronous validation errors for a field. */ abstract addAsyncErrorRule(logic: LogicFn): void; - /** Adds a rule to compute aggregate metadata for a field. */ - abstract addAggregateMetadataRule( - key: AggregateMetadataKey, - logic: LogicFn, - ): void; + /** Adds a rule to compute metadata for a field. */ + abstract addMetadataRule(key: MetadataKey, logic: LogicFn): void; - /** Adds a factory function to produce a data value associated with a field. */ - abstract addMetadataFactory(key: MetadataKey, factory: (ctx: FieldContext) => D): void; /** * Gets a builder for a child node associated with the given property key. * @param key The property key of the child. @@ -131,18 +122,8 @@ export class LogicNodeBuilder extends AbstractLogicNodeBuilder { this.getCurrent().addAsyncErrorRule(logic); } - override addAggregateMetadataRule( - key: AggregateMetadataKey, - logic: LogicFn, - ): void { - this.getCurrent().addAggregateMetadataRule(key, logic); - } - - override addMetadataFactory( - key: MetadataKey, - factory: (ctx: FieldContext) => D, - ): void { - this.getCurrent().addMetadataFactory(key, factory); + override addMetadataRule(key: MetadataKey, logic: LogicFn): void { + this.getCurrent().addMetadataRule(key, logic); } override getChild(key: PropertyKey): LogicNodeBuilder { @@ -271,18 +252,8 @@ class NonMergeableLogicNodeBuilder extends AbstractLogicNodeBuilder { this.logic.asyncErrors.push(setBoundPathDepthForResolution(logic, this.depth)); } - override addAggregateMetadataRule( - key: AggregateMetadataKey, - logic: LogicFn, - ): void { - this.logic.getAggregateMetadata(key).push(setBoundPathDepthForResolution(logic, this.depth)); - } - - override addMetadataFactory( - key: MetadataKey, - factory: (ctx: FieldContext) => D, - ): void { - this.logic.addMetadataFactory(key, setBoundPathDepthForResolution(factory, this.depth)); + override addMetadataRule(key: MetadataKey, logic: LogicFn): void { + this.logic.getMetadata(key).push(setBoundPathDepthForResolution(logic, this.depth)); } override getChild(key: PropertyKey): LogicNodeBuilder { @@ -464,7 +435,10 @@ function getAllChildBuilders( ...(builder.children.has(key) ? [{builder: builder.getChild(key), predicates: []}] : []), ]; } else { - throw new Error('Unknown LogicNodeBuilder type'); + throw new RuntimeError( + SignalFormsErrorCode.UNKNOWN_BUILDER_TYPE, + ngDevMode && 'Unknown LogicNodeBuilder type', + ); } } @@ -498,7 +472,10 @@ function createLogic( } else if (builder instanceof NonMergeableLogicNodeBuilder) { logic.mergeIn(builder.logic); } else { - throw new Error('Unknown LogicNodeBuilder type'); + throw new RuntimeError( + SignalFormsErrorCode.UNKNOWN_BUILDER_TYPE, + ngDevMode && 'Unknown LogicNodeBuilder type', + ); } return logic; } diff --git a/packages/forms/signals/src/schema/path_node.ts b/packages/forms/signals/src/schema/path_node.ts index e57f130a9a06..09ca4cfc253f 100644 --- a/packages/forms/signals/src/schema/path_node.ts +++ b/packages/forms/signals/src/schema/path_node.ts @@ -5,8 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ -import {SchemaPath, SchemaPathRules} from '../api/types'; -import {Predicate} from './logic'; +import type {SchemaPath, SchemaPathRules} from '../api/types'; +import type {Predicate} from './logic'; import {LogicNodeBuilder} from './logic_node'; import type {SchemaImpl} from './schema'; diff --git a/packages/forms/signals/src/schema/schema.ts b/packages/forms/signals/src/schema/schema.ts index 479ac13a2756..d5486b4c055c 100644 --- a/packages/forms/signals/src/schema/schema.ts +++ b/packages/forms/signals/src/schema/schema.ts @@ -6,6 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import {ɵRuntimeError as RuntimeError} from '@angular/core'; +import {SignalFormsErrorCode} from '../errors'; + import {SchemaPath, SchemaFn, SchemaOrSchemaFn} from '../api/types'; import {FieldPathNode} from './path_node'; @@ -105,9 +108,10 @@ export function isSchemaOrSchemaFn(value: unknown): value is SchemaOrSchemaFn): void { if (currentCompilingNode !== FieldPathNode.unwrapFieldPath(path).root) { - throw new Error( - `A FieldPath can only be used directly within the Schema that owns it,` + - ` **not** outside of it or within a sub-schema.`, + throw new RuntimeError( + SignalFormsErrorCode.PATH_OUTSIDE_SCHEMA, + ngDevMode && + `A FieldPath can only be used directly within the Schema that owns it, **not** outside of it or within a sub-schema.`, ); } } diff --git a/packages/forms/signals/test/node/api/metadata.spec.ts b/packages/forms/signals/test/node/api/metadata.spec.ts new file mode 100644 index 000000000000..e2c415aa4518 --- /dev/null +++ b/packages/forms/signals/test/node/api/metadata.spec.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Injector, signal} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; +import {createMetadataKey, form, metadata, MetadataReducer} from '../../../public_api'; + +describe('metadata', () => { + it('should reduce values with MetadataReducer.and', () => { + const KEY = createMetadataKey(MetadataReducer.and()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => true); + + metadata(p.y, KEY, () => true); + metadata(p.y, KEY, () => false); + metadata(p.y, KEY, () => true); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toBe(true); + expect(f.y().metadata(KEY)?.()).toBe(false); + }); + + it('should reduce values with MetadataReducer.or', () => { + const KEY = createMetadataKey(MetadataReducer.or()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => false); + + metadata(p.y, KEY, () => false); + metadata(p.y, KEY, () => true); + metadata(p.y, KEY, () => false); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toBe(false); + expect(f.y().metadata(KEY)?.()).toBe(true); + }); + + it('should reduce values with MetadataReducer.list', () => { + const KEY = createMetadataKey(MetadataReducer.list()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => 'a'); + + metadata(p.y, KEY, () => 'b'); + metadata(p.y, KEY, () => 'c'); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toEqual(['a']); + expect(f.y().metadata(KEY)?.()).toEqual(['b', 'c']); + }); + + it('should reduce values with MetadataReducer.max', () => { + const KEY = createMetadataKey(MetadataReducer.max()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => 10); + + metadata(p.y, KEY, () => 10); + metadata(p.y, KEY, () => 50); + metadata(p.y, KEY, () => 20); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toBe(10); + expect(f.y().metadata(KEY)?.()).toBe(50); + }); + + it('should reduce values with MetadataReducer.min', () => { + const KEY = createMetadataKey(MetadataReducer.min()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => 10); + + metadata(p.y, KEY, () => 10); + metadata(p.y, KEY, () => 5); + metadata(p.y, KEY, () => 20); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toBe(10); + expect(f.y().metadata(KEY)?.()).toBe(5); + }); + + it('should reduce values with MetadataReducer.override', () => { + const KEY = createMetadataKey(MetadataReducer.override()); + + const f = form( + signal({x: 0, y: 0}), + (p) => { + metadata(p.x, KEY, () => 10); + + metadata(p.y, KEY, () => 10); + metadata(p.y, KEY, () => 5); + metadata(p.y, KEY, () => 20); + }, + {injector: TestBed.inject(Injector)}, + ); + + expect(f().metadata(KEY)).toBe(undefined); + expect(f.x().metadata(KEY)?.()).toBe(10); + expect(f.y().metadata(KEY)?.()).toBe(20); + }); +}); diff --git a/packages/forms/signals/test/node/api/validators/email.spec.ts b/packages/forms/signals/test/node/api/validators/email.spec.ts index e03727696faa..cb6803811ec2 100644 --- a/packages/forms/signals/test/node/api/validators/email.spec.ts +++ b/packages/forms/signals/test/node/api/validators/email.spec.ts @@ -9,7 +9,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {email, form} from '../../../../public_api'; -import {customError, emailError} from '../../../../src/api/validation_errors'; +import {customError, emailError} from '../../../../src/api/rules/validation/validation_errors'; describe('email validator', () => { it('returns requiredTrue error when the value is false', () => { @@ -26,7 +26,7 @@ describe('email validator', () => { expect(f.email().errors()).toEqual([]); f.email().value.set('not-real-email'); - expect(f.email().errors()).toEqual([emailError({field: f.email})]); + expect(f.email().errors()).toEqual([emailError({fieldTree: f.email})]); }); it('supports custom errors', () => { @@ -46,7 +46,7 @@ describe('email validator', () => { expect(f.email().errors()).toEqual([ customError({ kind: 'special-email-pirojok-the-cat', - field: f.email, + fieldTree: f.email, }), ]); }); @@ -68,7 +68,7 @@ describe('email validator', () => { expect(f.email().errors()).toEqual([ emailError({ message: 'email error', - field: f.email, + fieldTree: f.email, }), ]); }); diff --git a/packages/forms/signals/test/node/api/validators/max.spec.ts b/packages/forms/signals/test/node/api/validators/max.spec.ts index 2188b6b375aa..accfe0de8ed4 100644 --- a/packages/forms/signals/test/node/api/validators/max.spec.ts +++ b/packages/forms/signals/test/node/api/validators/max.spec.ts @@ -8,8 +8,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {MAX, form, max} from '../../../../public_api'; -import {customError, maxError} from '../../../../src/api/validation_errors'; +import {customError, form, max, maxError} from '../../../../public_api'; describe('max validator', () => { it('returns max error when the value is larger', () => { @@ -22,7 +21,7 @@ describe('max validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); }); it('is inclusive', () => { @@ -70,7 +69,7 @@ describe('max validator', () => { customError({ kind: 'special-max', message: '6', - field: f.age, + fieldTree: f.age, }), ]); }); @@ -90,7 +89,7 @@ describe('max validator', () => { expect(f.age().errors()).toEqual([ maxError(5, { message: 'max error', - field: f.age, + fieldTree: f.age, }), ]); }); @@ -112,7 +111,7 @@ describe('max validator', () => { ); expect(f.age().errors()).toEqual([ - customError({kind: 'special-max', message: '6', field: f.age}), + customError({kind: 'special-max', message: '6', fieldTree: f.age}), ]); f.name().value.set('disabled'); expect(f.age().errors()).toEqual([]); @@ -162,7 +161,7 @@ describe('max validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().metadata(MAX)()).toBe(5); + expect(f.age().max?.()).toBe(5); }); it('merges two maxes preferring the smaller option', () => { @@ -177,13 +176,16 @@ describe('max validator', () => { ); f.age().value.set(12); - expect(f.age().errors()).toEqual([maxError(10, {field: f.age}), maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([ + maxError(10, {fieldTree: f.age}), + maxError(5, {fieldTree: f.age}), + ]); f.age().value.set(7); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); f.age().value.set(3); expect(f.age().errors()).toEqual([]); - expect(f.age().metadata(MAX)()).toBe(5); + expect(f.age().max?.()).toBe(5); }); it('merges two maxes _dynamically_ preferring the smaller option', () => { @@ -199,18 +201,21 @@ describe('max validator', () => { ); f.age().value.set(12); - expect(f.age().errors()).toEqual([maxError(10, {field: f.age}), maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([ + maxError(10, {fieldTree: f.age}), + maxError(5, {fieldTree: f.age}), + ]); f.age().value.set(7); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); f.age().value.set(3); expect(f.age().errors()).toEqual([]); - expect(f.age().metadata(MAX)()).toBe(5); + expect(f.age().max?.()).toBe(5); maxSignal.set(2); f.age().value.set(3); - expect(f.age().errors()).toEqual([maxError(2, {field: f.age})]); - expect(f.age().metadata(MAX)()).toBe(2); + expect(f.age().errors()).toEqual([maxError(2, {fieldTree: f.age})]); + expect(f.age().max?.()).toBe(2); }); it('merges two maxes _dynamically_ ignores undefined', () => { @@ -228,14 +233,14 @@ describe('max validator', () => { // Initially, age 20 is greater than both 10 and 15 expect(f.age().errors()).toEqual([ - maxError(10, {field: f.age}), - maxError(15, {field: f.age}), + maxError(10, {fieldTree: f.age}), + maxError(15, {fieldTree: f.age}), ]); // Set the first max threshold to undefined maxSignal.set(undefined); // Now, age 20 is only greater than 15 - expect(f.age().errors()).toEqual([maxError(15, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(15, {fieldTree: f.age})]); // Set the second max threshold to undefined maxSignal2.set(undefined); @@ -256,7 +261,7 @@ describe('max validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); maxValue.set(7); expect(f.age().errors()).toEqual([]); }); @@ -272,11 +277,11 @@ describe('max validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); maxValue.set(undefined); expect(f.age().errors()).toEqual([]); maxValue.set(5); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); }); it('handles dynamic value based on other field', () => { @@ -292,7 +297,7 @@ describe('max validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([maxError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([maxError(5, {fieldTree: f.age})]); f.name().value.set('other cat'); diff --git a/packages/forms/signals/test/node/api/validators/max_length.spec.ts b/packages/forms/signals/test/node/api/validators/max_length.spec.ts index a4e900140de0..9707d965674e 100644 --- a/packages/forms/signals/test/node/api/validators/max_length.spec.ts +++ b/packages/forms/signals/test/node/api/validators/max_length.spec.ts @@ -8,8 +8,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {MAX_LENGTH, form, maxLength} from '../../../../public_api'; -import {customError, maxLengthError} from '../../../../src/api/validation_errors'; +import {customError, form, maxLength, maxLengthError} from '../../../../public_api'; describe('maxLength validator', () => { it('returns maxLength error when the length is larger for strings', () => { @@ -22,7 +21,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([maxLengthError(3, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(3, {fieldTree: f.text})]); }); it('returns maxLength error when the length is larger for arrays', () => { @@ -35,7 +34,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.list().errors()).toEqual([maxLengthError(3, {field: f.list})]); + expect(f.list().errors()).toEqual([maxLengthError(3, {fieldTree: f.list})]); }); it('is inclusive (no error if length equals maxLength)', () => { @@ -85,7 +84,7 @@ describe('maxLength validator', () => { customError({ kind: 'special-maxLength', message: 'Length is 6', - field: f.text, + fieldTree: f.text, }), ]); }); @@ -105,7 +104,7 @@ describe('maxLength validator', () => { expect(f.text().errors()).toEqual([ maxLengthError(5, { message: 'abcdef is an error!', - field: f.text, + fieldTree: f.text, }), ]); }); @@ -120,7 +119,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f().errors()).toEqual([maxLengthError(3, {field: f})]); + expect(f().errors()).toEqual([maxLengthError(3, {fieldTree: f})]); }); it('should treat empty value as valid', () => { @@ -156,7 +155,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().metadata(MAX_LENGTH)()).toBe(5); + expect(f.text().maxLength?.()).toBe(5); }); it('merges two maxLengths preferring the smaller option', () => { @@ -172,17 +171,17 @@ describe('maxLength validator', () => { f.text().value.set('abcdefghijklmno'); expect(f.text().errors()).toEqual([ - maxLengthError(10, {field: f.text}), - maxLengthError(5, {field: f.text}), + maxLengthError(10, {fieldTree: f.text}), + maxLengthError(5, {fieldTree: f.text}), ]); f.text().value.set('abcdefg'); - expect(f.text().errors()).toEqual([maxLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(5, {fieldTree: f.text})]); f.text().value.set('abc'); expect(f.text().errors()).toEqual([]); - expect(f.text().metadata(MAX_LENGTH)()).toBe(5); + expect(f.text().maxLength?.()).toBe(5); }); it('merges two maxLengths _dynamically_ preferring the smaller option', () => { @@ -199,20 +198,20 @@ describe('maxLength validator', () => { f.text().value.set('abcdefghijklmno'); expect(f.text().errors()).toEqual([ - maxLengthError(10, {field: f.text}), - maxLengthError(5, {field: f.text}), + maxLengthError(10, {fieldTree: f.text}), + maxLengthError(5, {fieldTree: f.text}), ]); f.text().value.set('abcdefg'); - expect(f.text().errors()).toEqual([maxLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(5, {fieldTree: f.text})]); f.text().value.set('abc'); expect(f.text().errors()).toEqual([]); maxLengthSignal.set(2); - expect(f.text().errors()).toEqual([maxLengthError(2, {field: f.text})]); - expect(f.text().metadata(MAX_LENGTH)()).toBe(2); + expect(f.text().errors()).toEqual([maxLengthError(2, {fieldTree: f.text})]); + expect(f.text().maxLength?.()).toBe(2); }); }); @@ -228,7 +227,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([maxLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(5, {fieldTree: f.text})]); dynamicMaxLength.set(7); expect(f.text().errors()).toEqual([]); }); @@ -243,7 +242,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([maxLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(5, {fieldTree: f.text})]); dynamicMaxLength.set(undefined); expect(f.text().errors()).toEqual([]); }); @@ -260,7 +259,7 @@ describe('maxLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([maxLengthError(8, {field: f.text})]); + expect(f.text().errors()).toEqual([maxLengthError(8, {fieldTree: f.text})]); f.category().value.set('B'); expect(f.text().errors()).toEqual([]); diff --git a/packages/forms/signals/test/node/api/validators/min.spec.ts b/packages/forms/signals/test/node/api/validators/min.spec.ts index e445ff557987..763ba9ff81d0 100644 --- a/packages/forms/signals/test/node/api/validators/min.spec.ts +++ b/packages/forms/signals/test/node/api/validators/min.spec.ts @@ -8,8 +8,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {MIN, form, min} from '../../../../public_api'; -import {customError, minError} from '../../../../src/api/validation_errors'; +import {customError, form, min, minError} from '../../../../public_api'; describe('min validator', () => { it('returns min error when the value is smaller', () => { @@ -22,7 +21,7 @@ describe('min validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([minError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(5, {fieldTree: f.age})]); }); it('is inclusive', () => { @@ -70,7 +69,7 @@ describe('min validator', () => { customError({ kind: 'special-min', message: '3', - field: f.age, + fieldTree: f.age, }), ]); }); @@ -96,7 +95,7 @@ describe('min validator', () => { customError({ kind: 'special-min', message: '3', - field: f.age, + fieldTree: f.age, }), ]); }); @@ -116,7 +115,7 @@ describe('min validator', () => { expect(f.age().errors()).toEqual([ minError(5, { message: 'min error!!', - field: f.age, + fieldTree: f.age, }), ]); }); @@ -138,7 +137,7 @@ describe('min validator', () => { ); expect(f.age().errors()).toEqual([ - customError({kind: 'special-min', message: '3', field: f.age}), + customError({kind: 'special-min', message: '3', fieldTree: f.age}), ]); f.name().value.set('disabled'); expect(f.age().errors()).toEqual([]); @@ -184,7 +183,7 @@ describe('min validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().metadata(MIN)()).toBe(5); + expect(f.age().min?.()).toBe(5); }); it('merges two mins preferring the larger option', () => { @@ -199,13 +198,16 @@ describe('min validator', () => { ); f.age().value.set(3); - expect(f.age().errors()).toEqual([minError(5, {field: f.age}), minError(10, {field: f.age})]); + expect(f.age().errors()).toEqual([ + minError(5, {fieldTree: f.age}), + minError(10, {fieldTree: f.age}), + ]); f.age().value.set(7); - expect(f.age().errors()).toEqual([minError(10, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(10, {fieldTree: f.age})]); f.age().value.set(15); expect(f.age().errors()).toEqual([]); - expect(f.age().metadata(MIN)()).toBe(10); + expect(f.age().min?.()).toBe(10); }); it('merges two mins _dynamically_ preferring the larger option', () => { @@ -221,14 +223,17 @@ describe('min validator', () => { ); f.age().value.set(3); - expect(f.age().errors()).toEqual([minError(5, {field: f.age}), minError(10, {field: f.age})]); + expect(f.age().errors()).toEqual([ + minError(5, {fieldTree: f.age}), + minError(10, {fieldTree: f.age}), + ]); f.age().value.set(7); - expect(f.age().errors()).toEqual([minError(10, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(10, {fieldTree: f.age})]); f.age().value.set(15); expect(f.age().errors()).toEqual([]); minSignal.set(30); - expect(f.age().errors()).toEqual([minError(30, {field: f.age})]); - expect(f.age().metadata(MIN)()).toBe(30); + expect(f.age().errors()).toEqual([minError(30, {fieldTree: f.age})]); + expect(f.age().min?.()).toBe(30); }); it('merges two mins _dynamically_ ignores undefined', () => { @@ -245,11 +250,11 @@ describe('min validator', () => { ); expect(f.age().errors()).toEqual([ - minError(15, {field: f.age}), - minError(10, {field: f.age}), + minError(15, {fieldTree: f.age}), + minError(10, {fieldTree: f.age}), ]); minSignal.set(undefined); - expect(f.age().errors()).toEqual([minError(10, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(10, {fieldTree: f.age})]); minSignal2.set(undefined); expect(f.age().errors()).toEqual([]); }); @@ -267,11 +272,11 @@ describe('min validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([minError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(5, {fieldTree: f.age})]); minValue.set(undefined); expect(f.age().errors()).toEqual([]); minValue.set(5); - expect(f.age().errors()).toEqual([minError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(5, {fieldTree: f.age})]); }); it('handles dynamic value', () => { @@ -285,7 +290,7 @@ describe('min validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([minError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(5, {fieldTree: f.age})]); minValue.set(2); expect(f.age().errors()).toEqual([]); }); @@ -303,7 +308,7 @@ describe('min validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.age().errors()).toEqual([minError(5, {field: f.age})]); + expect(f.age().errors()).toEqual([minError(5, {fieldTree: f.age})]); f.name().value.set('other cat'); expect(f.age().errors()).toEqual([]); }); diff --git a/packages/forms/signals/test/node/api/validators/min_length.spec.ts b/packages/forms/signals/test/node/api/validators/min_length.spec.ts index 8e299a597a57..ec5afb1bcb7e 100644 --- a/packages/forms/signals/test/node/api/validators/min_length.spec.ts +++ b/packages/forms/signals/test/node/api/validators/min_length.spec.ts @@ -8,8 +8,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {MIN_LENGTH, form, minLength} from '../../../../public_api'; -import {customError, minLengthError} from '../../../../src/api/validation_errors'; +import {customError, form, minLength, minLengthError} from '../../../../public_api'; describe('minLength validator', () => { it('returns minLength error when the length is smaller for strings', () => { @@ -22,7 +21,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([minLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(5, {fieldTree: f.text})]); }); it('returns minLength error when the length is smaller for arrays', () => { @@ -35,7 +34,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.list().errors()).toEqual([minLengthError(5, {field: f.list})]); + expect(f.list().errors()).toEqual([minLengthError(5, {fieldTree: f.list})]); }); it('is inclusive (no error if length equals minLength)', () => { @@ -85,7 +84,7 @@ describe('minLength validator', () => { customError({ kind: 'special-minLength', message: 'Length is 2', - field: f.text, + fieldTree: f.text, }), ]); }); @@ -105,7 +104,7 @@ describe('minLength validator', () => { expect(f.text().errors()).toEqual([ minLengthError(5, { message: 'ab is error!', - field: f.text, + fieldTree: f.text, }), ]); }); @@ -120,7 +119,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f().errors()).toEqual([minLengthError(5, {field: f})]); + expect(f().errors()).toEqual([minLengthError(5, {fieldTree: f})]); }); it('should treat empty value as valid', () => { @@ -156,7 +155,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().metadata(MIN_LENGTH)()).toBe(5); + expect(f.text().minLength?.()).toBe(5); }); it('merges two minLengths preferring the larger option', () => { @@ -172,17 +171,17 @@ describe('minLength validator', () => { f.text().value.set('ab'); expect(f.text().errors()).toEqual([ - minLengthError(5, {field: f.text}), - minLengthError(10, {field: f.text}), + minLengthError(5, {fieldTree: f.text}), + minLengthError(10, {fieldTree: f.text}), ]); f.text().value.set('abcdefg'); - expect(f.text().errors()).toEqual([minLengthError(10, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(10, {fieldTree: f.text})]); f.text().value.set('abcdefghijklmno'); expect(f.text().errors()).toEqual([]); - expect(f.text().metadata(MIN_LENGTH)()).toBe(10); + expect(f.text().minLength?.()).toBe(10); }); it('merges two minLengths _dynamically_ preferring the larger option', () => { @@ -199,20 +198,20 @@ describe('minLength validator', () => { f.text().value.set('ab'); expect(f.text().errors()).toEqual([ - minLengthError(5, {field: f.text}), - minLengthError(10, {field: f.text}), + minLengthError(5, {fieldTree: f.text}), + minLengthError(10, {fieldTree: f.text}), ]); f.text().value.set('abcdefg'); - expect(f.text().errors()).toEqual([minLengthError(10, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(10, {fieldTree: f.text})]); f.text().value.set('abcdefghijklmno'); expect(f.text().errors()).toEqual([]); minLengthSignal.set(20); - expect(f.text().errors()).toEqual([minLengthError(20, {field: f.text})]); - expect(f.text().metadata(MIN_LENGTH)()).toBe(20); + expect(f.text().errors()).toEqual([minLengthError(20, {fieldTree: f.text})]); + expect(f.text().minLength?.()).toBe(20); }); }); @@ -228,7 +227,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([minLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(5, {fieldTree: f.text})]); dynamicMinLength.set(3); expect(f.text().errors()).toEqual([]); }); @@ -244,7 +243,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([minLengthError(5, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(5, {fieldTree: f.text})]); dynamicMinLength.set(undefined); expect(f.text().errors()).toEqual([]); }); @@ -261,7 +260,7 @@ describe('minLength validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.text().errors()).toEqual([minLengthError(8, {field: f.text})]); + expect(f.text().errors()).toEqual([minLengthError(8, {fieldTree: f.text})]); f.category().value.set('B'); expect(f.text().errors()).toEqual([]); diff --git a/packages/forms/signals/test/node/api/validators/pattern.spec.ts b/packages/forms/signals/test/node/api/validators/pattern.spec.ts index 69d7fd2a0642..c48f6ed6373b 100644 --- a/packages/forms/signals/test/node/api/validators/pattern.spec.ts +++ b/packages/forms/signals/test/node/api/validators/pattern.spec.ts @@ -8,8 +8,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {PATTERN, form, pattern} from '../../../../public_api'; -import {customError, patternError} from '../../../../src/api/validation_errors'; +import {customError, form, pattern, patternError} from '../../../../public_api'; describe('pattern validator', () => { it('validates whether a value matches the pattern', () => { @@ -22,7 +21,7 @@ describe('pattern validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.name().errors()).toEqual([patternError(/pir.*jok/, {field: f.name})]); + expect(f.name().errors()).toEqual([patternError(/pir.*jok/, {fieldTree: f.name})]); }); it('supports custom error', () => { @@ -35,7 +34,7 @@ describe('pattern validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.name().errors()).toEqual([customError({field: f.name})]); + expect(f.name().errors()).toEqual([customError({fieldTree: f.name})]); }); it('supports custom error message', () => { @@ -49,7 +48,7 @@ describe('pattern validator', () => { ); expect(f.name().errors()).toEqual([ - patternError(/pir.*jok/, {message: 'pattern error', field: f.name}), + patternError(/pir.*jok/, {message: 'pattern error', fieldTree: f.name}), ]); }); @@ -79,7 +78,7 @@ describe('pattern validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.name().metadata(PATTERN)()).toEqual([/pir.*jok/]); + expect(f.name().pattern()).toEqual([/pir.*jok/]); }); it('merges the PATTERN property in an array', () => { @@ -93,7 +92,7 @@ describe('pattern validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.name().metadata(PATTERN)()).toEqual([/pir.*jok/, /pelmeni/]); + expect(f.name().pattern()).toEqual([/pir.*jok/, /pelmeni/]); }); it('PATTERN property defaults to empty list', () => { @@ -105,7 +104,7 @@ describe('pattern validator', () => { }, {injector: TestBed.inject(Injector)}, ); - expect(f.name().metadata(PATTERN)()).toEqual([]); + expect(f.name().pattern()).toEqual([]); }); }); @@ -121,12 +120,12 @@ describe('pattern validator', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.name().errors()).toEqual([patternError(/pir.*jok/, {field: f.name})]); + expect(f.name().errors()).toEqual([patternError(/pir.*jok/, {fieldTree: f.name})]); patternSignal.set(/p.*/); expect(f.name().errors()).toEqual([]); patternSignal.set(/meow/); - expect(f.name().errors()).toEqual([patternError(/meow/, {field: f.name})]); + expect(f.name().errors()).toEqual([patternError(/meow/, {fieldTree: f.name})]); patternSignal.set(undefined); diff --git a/packages/forms/signals/test/node/api/validators/required.spec.ts b/packages/forms/signals/test/node/api/validators/required.spec.ts index b6a031168fee..207508de630b 100644 --- a/packages/forms/signals/test/node/api/validators/required.spec.ts +++ b/packages/forms/signals/test/node/api/validators/required.spec.ts @@ -9,7 +9,7 @@ import {Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {form, required} from '../../../../public_api'; -import {customError, requiredError} from '../../../../src/api/validation_errors'; +import {customError, requiredError} from '../../../../src/api/rules/validation/validation_errors'; describe('required validator', () => { it('returns required Error when the value is not present', () => { @@ -24,7 +24,7 @@ describe('required validator', () => { }, ); - expect(f.name().errors()).toEqual([requiredError({field: f.name})]); + expect(f.name().errors()).toEqual([requiredError({fieldTree: f.name})]); f.name().value.set('pirojok-the-cat'); expect(f.name().errors()).toEqual([]); }); @@ -43,7 +43,7 @@ describe('required validator', () => { }, ); - expect(f.name().errors()).toEqual([customError({kind: 'required-5', field: f.name})]); + expect(f.name().errors()).toEqual([customError({kind: 'required-5', fieldTree: f.name})]); f.name().value.set('pirojok-the-cat'); expect(f.name().errors()).toEqual([]); }); @@ -62,7 +62,9 @@ describe('required validator', () => { }, ); - expect(f.name().errors()).toEqual([requiredError({message: 'required error', field: f.name})]); + expect(f.name().errors()).toEqual([ + requiredError({message: 'required error', fieldTree: f.name}), + ]); f.name().value.set('pirojok-the-cat'); expect(f.name().errors()).toEqual([]); }); @@ -85,7 +87,7 @@ describe('required validator', () => { expect(f.name().errors()).toEqual([]); f.age().value.set(15); - expect(f.name().errors()).toEqual([requiredError({field: f.name})]); + expect(f.name().errors()).toEqual([requiredError({fieldTree: f.name})]); }); it('supports returning custom plain error, and wraps it as custom', () => { @@ -106,6 +108,8 @@ describe('required validator', () => { expect(f.name().errors()).toEqual([]); f.name().value.set(''); - expect(f.name().errors()).toEqual([customError({kind: 'pirojok-the-error', field: f.name})]); + expect(f.name().errors()).toEqual([ + customError({kind: 'pirojok-the-error', fieldTree: f.name}), + ]); }); }); diff --git a/packages/forms/signals/test/node/api/validators/util.spec.ts b/packages/forms/signals/test/node/api/validators/util.spec.ts index d931de0ae156..1e7dfc034b89 100644 --- a/packages/forms/signals/test/node/api/validators/util.spec.ts +++ b/packages/forms/signals/test/node/api/validators/util.spec.ts @@ -6,9 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ +import {ensureCustomValidationResult} from '../../../../src/api/rules/validation/util'; +import { + customError, + minError, + ValidationError, +} from '../../../../src/api/rules/validation/validation_errors'; import {FieldTree} from '../../../../src/api/types'; -import {customError, minError, ValidationError} from '../../../../src/api/validation_errors'; -import {ensureCustomValidationResult} from '../../../../src/api/validators/util'; import {addDefaultField} from '../../../../src/field/validation'; describe('validators utils', () => { @@ -22,7 +26,7 @@ describe('validators utils', () => { }); it('should wrap a plain error object with customError', () => { - const error: ValidationError.WithField = {kind: 'meow', field: {} as FieldTree}; + const error: ValidationError.WithField = {kind: 'meow', fieldTree: {} as FieldTree}; const result = ensureCustomValidationResult(error); expect(result).toEqual(customError(error)); }); @@ -65,7 +69,7 @@ describe('validators utils', () => { it('should process a mixed array of validation errors', () => { const plainError: ValidationError.WithField = { kind: 'plain', - field: {} as FieldTree, + fieldTree: {} as FieldTree, }; const custom = customError({kind: 'custom'}); diff --git a/packages/forms/signals/test/node/api/validators/validation_errors.spec.ts b/packages/forms/signals/test/node/api/validators/validation_errors.spec.ts index 712a1fb63f41..ffaf6804337a 100644 --- a/packages/forms/signals/test/node/api/validators/validation_errors.spec.ts +++ b/packages/forms/signals/test/node/api/validators/validation_errors.spec.ts @@ -10,14 +10,14 @@ import {Injector, signal} from '@angular/core'; import {form} from '../../../../src/api/structure'; import {TestBed} from '@angular/core/testing'; -import {validate} from '../../../../src/api/logic'; +import {validate} from '../../../../src/api/rules'; import { customError, CustomValidationError, minError, MinValidationError, ValidationError, -} from '../../../../src/api/validation_errors'; +} from '../../../../src/api/rules/validation/validation_errors'; import {FieldTree, FieldValidator, PathKind} from '../../../../src/api/types'; import Root = PathKind.Root; diff --git a/packages/forms/signals/test/node/api/when.spec.ts b/packages/forms/signals/test/node/api/when.spec.ts index 3039ce294f1a..b94ee9e0be48 100644 --- a/packages/forms/signals/test/node/api/when.spec.ts +++ b/packages/forms/signals/test/node/api/when.spec.ts @@ -16,7 +16,7 @@ import { form, validate, } from '../../../public_api'; -import {customError, requiredError} from '../../../src/api/validation_errors'; +import {customError, requiredError} from '../../../src/api/rules/validation/validation_errors'; export interface User { first: string; @@ -43,7 +43,7 @@ describe('when', () => { f().value.set({first: 'meow', needLastName: false, last: ''}); expect(f.last().errors()).toEqual([]); f().value.set({first: 'meow', needLastName: true, last: ''}); - expect(f.last().errors()).toEqual([requiredError({field: f.last})]); + expect(f.last().errors()).toEqual([requiredError({fieldTree: f.last})]); }); it('Disallows using non-local paths', () => { @@ -89,12 +89,12 @@ describe('when', () => { ); f.needLastName().value.set(true); expect(f.items[0].last().errors()).toEqual([ - customError({kind: 'required1', field: f.items[0].last}), - customError({kind: 'required2', field: f.items[0].last}), + customError({kind: 'required1', fieldTree: f.items[0].last}), + customError({kind: 'required2', fieldTree: f.items[0].last}), ]); f.needLastName().value.set(false); expect(f.items[0].last().errors()).toEqual([ - customError({kind: 'required1', field: f.items[0].last}), + customError({kind: 'required1', fieldTree: f.items[0].last}), ]); }); @@ -115,7 +115,7 @@ describe('when', () => { f().value.set({first: 'meow', needLastName: false, last: ''}); expect(f.last().errors()).toEqual([]); f().value.set({first: 'meow', needLastName: true, last: ''}); - expect(f.last().errors()).toEqual([requiredError({field: f.last})]); + expect(f.last().errors()).toEqual([requiredError({fieldTree: f.last})]); }); it('supports mix of conditional and non conditional validators', () => { @@ -135,11 +135,11 @@ describe('when', () => { ); f().value.set({first: 'meow', needLastName: false, last: ''}); - expect(f.last().errors()).toEqual([customError({kind: 'short', field: f.last})]); + expect(f.last().errors()).toEqual([customError({kind: 'short', fieldTree: f.last})]); f().value.set({first: 'meow', needLastName: true, last: ''}); expect(f.last().errors()).toEqual([ - customError({kind: 'short', field: f.last}), - requiredError({field: f.last}), + customError({kind: 'short', fieldTree: f.last}), + requiredError({fieldTree: f.last}), ]); }); @@ -161,7 +161,7 @@ describe('when', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.items[0].last().errors()).toEqual([requiredError({field: f.items[0].last})]); + expect(f.items[0].last().errors()).toEqual([requiredError({fieldTree: f.items[0].last})]); f.needLastName().value.set(false); expect(f.items[0].last().errors()).toEqual([]); }); @@ -186,11 +186,17 @@ describe('applyWhenValue', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.numOrNull().errors()).toEqual([customError({kind: 'too-small', field: f.numOrNull})]); + expect(f.numOrNull().errors()).toEqual([ + customError({kind: 'too-small', fieldTree: f.numOrNull}), + ]); f.numOrNull().value.set(5); - expect(f.numOrNull().errors()).toEqual([customError({kind: 'too-small', field: f.numOrNull})]); + expect(f.numOrNull().errors()).toEqual([ + customError({kind: 'too-small', fieldTree: f.numOrNull}), + ]); f.numOrNull().value.set(null); - expect(f.numOrNull().errors()).toEqual([customError({kind: 'too-small', field: f.numOrNull})]); + expect(f.numOrNull().errors()).toEqual([ + customError({kind: 'too-small', fieldTree: f.numOrNull}), + ]); f.numOrNull().value.set(15); expect(f.numOrNull().errors()).toEqual([]); }); @@ -215,7 +221,9 @@ describe('applyWhenValue', () => { expect(f.numOrNull().errors()).toEqual([]); f.numOrNull().value.set(5); - expect(f.numOrNull().errors()).toEqual([customError({kind: 'too-small', field: f.numOrNull})]); + expect(f.numOrNull().errors()).toEqual([ + customError({kind: 'too-small', fieldTree: f.numOrNull}), + ]); f.numOrNull().value.set(null); expect(f.numOrNull().errors()).toEqual([]); f.numOrNull().value.set(15); diff --git a/packages/forms/signals/test/node/compat/compat.spec.ts b/packages/forms/signals/test/node/compat/compat.spec.ts index 1f2f0f7ca73a..b75e78a89d8e 100644 --- a/packages/forms/signals/test/node/compat/compat.spec.ts +++ b/packages/forms/signals/test/node/compat/compat.spec.ts @@ -9,8 +9,7 @@ import {ApplicationRef, Injector, signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {FormControl, FormGroup, Validators} from '@angular/forms'; -import {compatForm} from '../../../compat/src/api/compat_form'; -import {CompatValidationError} from '../../../compat/src/api/compat_validation_error'; +import {compatForm, CompatValidationError} from '../../../compat/public_api'; import { customError, disabled, @@ -19,7 +18,6 @@ import { FieldTree, form, hidden, - metadata, readonly, required, submit, @@ -459,7 +457,7 @@ describe('Forms compat', () => { }, ); - expect(f().errors()).toEqual([customError({kind: 'too small', field: f})]); + expect(f().errors()).toEqual([customError({kind: 'too small', fieldTree: f})]); }); it('supports getting control from fieldTreeOf', () => { @@ -486,7 +484,7 @@ describe('Forms compat', () => { }, ); - expect(f().errors()).toEqual([customError({kind: 'too small', field: f})]); + expect(f().errors()).toEqual([customError({kind: 'too small', fieldTree: f})]); }); it('fails for regular values', () => { @@ -552,7 +550,7 @@ describe('Forms compat', () => { expect(f.name().errors()).toEqual([ customError({ kind: 'too small', - field: f.name, + fieldTree: f.name, }), ]); @@ -720,10 +718,6 @@ describe('Forms compat', () => { return valueOf(path.age) < 8; }); - metadata(path.name, ({valueOf}) => { - return valueOf(path.age) < 8 ? '' : ''; - }); - email(path.name, { error: ({valueOf}) => { return valueOf(path.age) < 8 ? [] : []; diff --git a/packages/forms/signals/test/node/field_context.spec.ts b/packages/forms/signals/test/node/field_context.spec.ts index 26c76a9b2bc5..2d2fe8f4b039 100644 --- a/packages/forms/signals/test/node/field_context.spec.ts +++ b/packages/forms/signals/test/node/field_context.spec.ts @@ -9,12 +9,12 @@ import {Injector, signal, WritableSignal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import { - aggregateMetadata, applyEach, + createMetadataKey, FieldContext, form, + metadata, PathKind, - reducedMetadataKey, SchemaPath, SchemaPathTree, validate, @@ -59,8 +59,8 @@ describe('Field Context', () => { it('field', () => { const cat = signal({name: 'pirojok-the-cat', age: 5}); testContext(cat, (ctx) => { - expect(ctx.field.name().value()).toEqual('pirojok-the-cat'); - expect(ctx.field.age().value()).toEqual(5); + expect(ctx.fieldTree.name().value()).toEqual('pirojok-the-cat'); + expect(ctx.fieldTree.age().value()).toEqual(5); }); }); @@ -87,7 +87,7 @@ describe('Field Context', () => { ); f().valid(); expect(keys).toEqual([ - 'RuntimeError: the top-level field in the form has no parent', + jasmine.stringContaining('NG01905'), // SIGNAL_FORMS_ROOT_FIELD_NO_PARENT 'name', 'age', ]); @@ -125,32 +125,32 @@ describe('Field Context', () => { ); f().valid(); expect(indices).toEqual([ - 'RuntimeError: the top-level field in the form has no parent', + jasmine.stringContaining('NG01905'), // SIGNAL_FORMS_ROOT_FIELD_NO_PARENT 0, 1, - 'RuntimeError: cannot access index, parent field is not an array', + jasmine.stringContaining('NG01906'), // SIGNAL_FORMS_PARENT_NOT_ARRAY ]); }); it('pathKeys', () => { - const KEYS = reducedMetadataKey( - (_: readonly string[], n: readonly string[]) => n, - () => [], - ); + const KEYS = createMetadataKey({ + reduce: (_: readonly string[], n: readonly string[]) => n, + getInitial: () => [], + }); const f = form( signal({x: [1]}), (p) => { - aggregateMetadata(p, KEYS, ({pathKeys}) => pathKeys()); - aggregateMetadata(p.x, KEYS, ({pathKeys}) => pathKeys()); + metadata(p, KEYS, ({pathKeys}) => pathKeys()); + metadata(p.x, KEYS, ({pathKeys}) => pathKeys()); applyEach(p.x, (it) => { - aggregateMetadata(it, KEYS, ({pathKeys}) => pathKeys()); + metadata(it, KEYS, ({pathKeys}) => pathKeys()); }); }, {injector: TestBed.inject(Injector)}, ); - expect(f().metadata(KEYS)()).toEqual([]); - expect(f.x().metadata(KEYS)()).toEqual(['x']); - expect(f.x[0]().metadata(KEYS)()).toEqual(['x', '0']); + expect(f().metadata(KEYS)?.()).toEqual([]); + expect(f.x().metadata(KEYS)?.()).toEqual(['x']); + expect(f.x[0]().metadata(KEYS)?.()).toEqual(['x', '0']); }); it('valueOf', () => { diff --git a/packages/forms/signals/test/node/field_node.spec.ts b/packages/forms/signals/test/node/field_node.spec.ts index 577f2e6f59ed..9fdfb9dd5ea8 100644 --- a/packages/forms/signals/test/node/field_node.spec.ts +++ b/packages/forms/signals/test/node/field_node.spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {computed, effect, Injector, signal} from '@angular/core'; +import {computed, effect, Injector, signal, WritableSignal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import { apply, @@ -17,7 +17,6 @@ import { hidden, readonly, required, - REQUIRED, requiredError, Schema, schema, @@ -122,6 +121,41 @@ describe('FieldNode', () => { f().reset(); expect(f.a().value()).toBe(1); }); + + it('can reset with empty string', () => { + const model = signal('hello'); + const f = form(model, {injector: TestBed.inject(Injector)}); + f().reset(''); + expect(f().value()).toBe(''); + }); + + it('can reset with false', () => { + const model = signal(true); + const f = form(model, {injector: TestBed.inject(Injector)}); + f().reset(false); + expect(f().value()).toBe(false); + }); + + it('can reset with null', () => { + const model: WritableSignal = signal('hello'); + const f = form(model, {injector: TestBed.inject(Injector)}); + f().reset(null); + expect(f().value()).toBeNull(); + }); + + it('can reset with 0', () => { + const model = signal(5); + const f = form(model, {injector: TestBed.inject(Injector)}); + f().reset(0); + expect(f().value()).toBe(0); + }); + + it('can reset with NaN', () => { + const model = signal(5); + const f = form(model, {injector: TestBed.inject(Injector)}); + f().reset(NaN); + expect(f().value()).toBeNaN(); + }); }); describe('dirty', () => { @@ -717,7 +751,7 @@ describe('FieldNode', () => { const a = f.a; expect(f().disabled()).toBe(false); expect(a().disabled()).toBe(true); - expect(a().disabledReasons()).toEqual([{field: f.a}]); + expect(a().disabledReasons()).toEqual([{fieldTree: f.a}]); a().value.set(2); expect(f().disabled()).toBe(false); @@ -736,7 +770,7 @@ describe('FieldNode', () => { expect(f.a().disabled()).toBe(true); expect(f.a().disabledReasons()).toEqual([ { - field: f.a, + fieldTree: f.a, message: 'a cannot be changed', }, ]); @@ -759,7 +793,7 @@ describe('FieldNode', () => { expect(f.a().disabled()).toBe(true); expect(f.a().disabledReasons()).toEqual([ { - field: f.a, + fieldTree: f.a, message: 'a cannot be changed', }, ]); @@ -777,14 +811,14 @@ describe('FieldNode', () => { expect(f().disabled()).toBe(true); expect(f().disabledReasons()).toEqual([ { - field: f, + fieldTree: f, message: 'form unavailable', }, ]); expect(f.a().disabled()).toBe(true); expect(f.a().disabledReasons()).toEqual([ { - field: f, + fieldTree: f, message: 'form unavailable', }, ]); @@ -802,12 +836,12 @@ describe('FieldNode', () => { expect(f.a().disabledReasons()).toEqual([ { - field: f.a, + fieldTree: f.a, }, ]); expect(f.b().disabledReasons()).toEqual([ { - field: f.b, + fieldTree: f.b, message: 'disabled!', }, ]); @@ -869,12 +903,12 @@ describe('FieldNode', () => { {injector: TestBed.inject(Injector)}, ); - expect(f().metadata(REQUIRED)()).toBe(true); + expect(f().required()).toBe(true); expect(f().valid()).toBe(false); expect(f().readonly()).toBe(false); isReadonly.set(true); - expect(f().metadata(REQUIRED)()).toBe(true); + expect(f().required()).toBe(true); expect(f().valid()).toBe(true); expect(f().readonly()).toBe(true); }); @@ -901,7 +935,7 @@ describe('FieldNode', () => { expect(f().valid()).toBe(true); f.a().value.set(11); - expect(f.a().errors()).toEqual([customError({kind: 'too-damn-high', field: f.a})]); + expect(f.a().errors()).toEqual([customError({kind: 'too-damn-high', fieldTree: f.a})]); expect(f.a().valid()).toBe(false); expect(f().errors()).toEqual([]); expect(f().valid()).toBe(false); @@ -926,8 +960,8 @@ describe('FieldNode', () => { f.a().value.set(11); expect(f.a().errors()).toEqual([ - customError({kind: 'too-damn-high', field: f.a}), - customError({kind: 'bad', field: f.a}), + customError({kind: 'too-damn-high', fieldTree: f.a}), + customError({kind: 'bad', fieldTree: f.a}), ]); expect(f.a().valid()).toBe(false); }); @@ -942,15 +976,15 @@ describe('FieldNode', () => { {injector: TestBed.inject(Injector)}, ); - expect(f.first().errors()).toEqual([requiredError({field: f.first})]); + expect(f.first().errors()).toEqual([requiredError({fieldTree: f.first})]); expect(f.first().valid()).toBe(false); - expect(f.first().metadata(REQUIRED)()).toBe(true); + expect(f.first().required()).toBe(true); f.first().value.set('Bob'); expect(f.first().errors()).toEqual([]); expect(f.first().valid()).toBe(true); - expect(f.first().metadata(REQUIRED)()).toBe(true); + expect(f.first().required()).toBe(true); }); it('should validate conditionally required field', () => { @@ -966,19 +1000,19 @@ describe('FieldNode', () => { expect(f.first().errors()).toEqual([]); expect(f.first().valid()).toBe(true); - expect(f.first().metadata(REQUIRED)()).toBe(false); + expect(f.first().required()).toBe(false); f.last().value.set('Loblaw'); - expect(f.first().errors()).toEqual([requiredError({field: f.first})]); + expect(f.first().errors()).toEqual([requiredError({fieldTree: f.first})]); expect(f.first().valid()).toBe(false); - expect(f.first().metadata(REQUIRED)()).toBe(true); + expect(f.first().required()).toBe(true); f.first().value.set('Bob'); expect(f.first().errors()).toEqual([]); expect(f.first().valid()).toBe(true); - expect(f.first().metadata(REQUIRED)()).toBe(true); + expect(f.first().required()).toBe(true); }); it('should link required error messages to their predicate', () => { @@ -1006,7 +1040,7 @@ describe('FieldNode', () => { expect(f.name().errors()).toEqual([ requiredError({ message: 'Name is required in your country', - field: f.name, + fieldTree: f.name, }), ]); @@ -1014,11 +1048,11 @@ describe('FieldNode', () => { expect(f.name().errors()).toEqual([ requiredError({ message: 'Name is required in your country', - field: f.name, + fieldTree: f.name, }), requiredError({ message: 'Name is required for large transactions', - field: f.name, + fieldTree: f.name, }), ]); @@ -1026,7 +1060,7 @@ describe('FieldNode', () => { expect(f.name().errors()).toEqual([ requiredError({ message: 'Name is required for large transactions', - field: f.name, + fieldTree: f.name, }), ]); @@ -1047,7 +1081,7 @@ describe('FieldNode', () => { expect(f.a().valid()).toBe(true); f.a().value.set(2); - expect(f.a().errors()).toEqual([customError({field: f.a})]); + expect(f.a().errors()).toEqual([customError({fieldTree: f.a})]); expect(f.a().valid()).toBe(false); }); @@ -1060,10 +1094,10 @@ describe('FieldNode', () => { validateTree(p, ({value, fieldTreeOf}) => { const errors: ValidationError[] = []; if (value().name.length > 8) { - errors.push(customError({kind: 'long_name', field: fieldTreeOf(p.name)})); + errors.push(customError({kind: 'long_name', fieldTree: fieldTreeOf(p.name)})); } if (value().age < 0) { - errors.push(customError({kind: 'temporal_anomaly', field: fieldTreeOf(p.age)})); + errors.push(customError({kind: 'temporal_anomaly', fieldTree: fieldTreeOf(p.age)})); } return errors; }); @@ -1077,10 +1111,12 @@ describe('FieldNode', () => { f.age().value.set(-10); expect(f.name().errors()).toEqual([]); - expect(f.age().errors()).toEqual([customError({kind: 'temporal_anomaly', field: f.age})]); + expect(f.age().errors()).toEqual([ + customError({kind: 'temporal_anomaly', fieldTree: f.age}), + ]); cat.set({name: 'Fluffy McFluffington', age: 10}); - expect(f.name().errors()).toEqual([customError({kind: 'long_name', field: f.name})]); + expect(f.name().errors()).toEqual([customError({kind: 'long_name', fieldTree: f.name})]); expect(f.age().errors()).toEqual([]); }); @@ -1092,10 +1128,10 @@ describe('FieldNode', () => { validateTree(p, ({value, fieldTreeOf}) => { const errors: ValidationError[] = []; if (value().name.length > 8) { - errors.push(customError({kind: 'long_name', field: fieldTreeOf(p.name)})); + errors.push(customError({kind: 'long_name', fieldTree: fieldTreeOf(p.name)})); } if (value().age < 0) { - errors.push(customError({kind: 'temporal_anomaly', field: fieldTreeOf(p.age)})); + errors.push(customError({kind: 'temporal_anomaly', fieldTree: fieldTreeOf(p.age)})); } return errors; }); @@ -1109,10 +1145,12 @@ describe('FieldNode', () => { f.age().value.set(-10); expect(f.name().errors()).toEqual([]); - expect(f.age().errors()).toEqual([customError({kind: 'temporal_anomaly', field: f.age})]); + expect(f.age().errors()).toEqual([ + customError({kind: 'temporal_anomaly', fieldTree: f.age}), + ]); cat.set({name: 'Fluffy McFluffington', age: 10}); - expect(f.name().errors()).toEqual([customError({kind: 'long_name', field: f.name})]); + expect(f.name().errors()).toEqual([customError({kind: 'long_name', fieldTree: f.name})]); expect(f.age().errors()).toEqual([]); }); }); @@ -1136,7 +1174,7 @@ describe('FieldNode', () => { {injector: TestBed.inject(Injector)}, ); - expect(f().errorSummary()).toEqual([requiredError({field: f})]); + expect(f().errorSummary()).toEqual([requiredError({fieldTree: f})]); }); it('should contain errors from child fields', () => { @@ -1151,8 +1189,8 @@ describe('FieldNode', () => { ); expect(f().errorSummary()).toEqual([ - requiredError({field: f.first}), - requiredError({field: f.last}), + requiredError({fieldTree: f.first}), + requiredError({fieldTree: f.last}), ]); }); @@ -1173,16 +1211,16 @@ describe('FieldNode', () => { ); expect(f.child.child().errorSummary()).toEqual([ - customError({kind: 'grandchild', field: f.child.child}), + customError({kind: 'grandchild', fieldTree: f.child.child}), ]); expect(f.child().errorSummary()).toEqual([ - customError({kind: 'child', field: f.child}), - customError({kind: 'grandchild', field: f.child.child}), + customError({kind: 'child', fieldTree: f.child}), + customError({kind: 'grandchild', fieldTree: f.child.child}), ]); expect(f().errorSummary()).toEqual([ - customError({kind: 'root', field: f}), - customError({kind: 'child', field: f.child}), - customError({kind: 'grandchild', field: f.child.child}), + customError({kind: 'root', fieldTree: f}), + customError({kind: 'child', fieldTree: f.child}), + customError({kind: 'grandchild', fieldTree: f.child.child}), ]); }); }); @@ -1285,7 +1323,7 @@ describe('FieldNode', () => { {injector: TestBed.inject(Injector)}, ); - expect(() => f().disabled()).toThrowError('Path is not part of this field tree.'); + expect(() => f().disabled()).toThrowError(/Path is not part of this field tree\./); }); }); diff --git a/packages/forms/signals/test/node/field_proxy.spec.ts b/packages/forms/signals/test/node/field_proxy.spec.ts index be7d058e75d2..9f13d12799f6 100644 --- a/packages/forms/signals/test/node/field_proxy.spec.ts +++ b/packages/forms/signals/test/node/field_proxy.spec.ts @@ -6,9 +6,20 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Injector, signal} from '@angular/core'; +import { + Component, + Input, + Injector, + input, + signal, + ApplicationRef, + effect, + untracked, + computed, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {form} from '../../public_api'; +import {form, FieldTree} from '../../public_api'; +import {ChangeDetectionStrategy} from '@angular/compiler'; describe('FieldTree proxy', () => { it('should not forward methods through the proxy', () => { @@ -54,6 +65,56 @@ describe('FieldTree proxy', () => { expect(Object.values(f).map((child) => child().value())).toEqual([1, 2]); }); + it('should be reactive when children change, but not on value mutations', async () => { + const injector = TestBed.inject(Injector); + const appRef = injector.get(ApplicationRef); + const value = signal<{a: number; b?: number}>({a: 1}); + const f = form(value, {injector}); + expect(f.b).not.toBeDefined(); + + const log: string[] = []; + + effect( + () => { + log.push(`a: ${f.a().value()}`); + }, + {injector}, + ); + await appRef.whenStable(); + expect(log).toEqual(['a: 1']); + + value.set({a: 1, b: 2}); + await appRef.whenStable(); + expect(log).toEqual(['a: 1']); + + value.set({a: 2, b: 2}); + await appRef.whenStable(); + expect(log).toEqual(['a: 1', 'a: 2']); + }); + + it('should be reactive to array values swapping', async () => { + const injector = TestBed.inject(Injector); + const appRef = injector.get(ApplicationRef); + const value = signal([{value: 1}, {value: 2}]); + const f = form(value, {injector}); + + const log: string[] = []; + + effect( + () => { + log.push(`[${untracked(f[0]().value).value}, ${untracked(f[1]().value).value}]`); + }, + {injector}, + ); + await appRef.whenStable(); + expect(log).toEqual(['[1, 2]']); + + value.update((v) => [v[1], v[0]]); + + appRef.tick(); + expect(log).toEqual(['[1, 2]', '[2, 1]']); + }); + it('should get keys and values for primitive field', () => { const f = form(signal(1), {injector: TestBed.inject(Injector)}); expect(Object.keys(f)).toEqual([]); diff --git a/packages/forms/signals/test/node/logic_node.spec.ts b/packages/forms/signals/test/node/logic_node.spec.ts index b69a7ea783b4..525644053195 100644 --- a/packages/forms/signals/test/node/logic_node.spec.ts +++ b/packages/forms/signals/test/node/logic_node.spec.ts @@ -19,7 +19,7 @@ const fakeFieldContext: FieldContext = { structure: {pathKeys: () => [], parent: undefined}, }) as any, valueOf: () => undefined!, - field: undefined!, + fieldTree: undefined!, state: undefined!, value: undefined!, pathKeys: computed(() => []), diff --git a/packages/forms/signals/test/node/recursive_logic.spec.ts b/packages/forms/signals/test/node/recursive_logic.spec.ts index c45450a69148..837f4a076f25 100644 --- a/packages/forms/signals/test/node/recursive_logic.spec.ts +++ b/packages/forms/signals/test/node/recursive_logic.spec.ts @@ -9,7 +9,7 @@ import {computed, Injector, signal, type Signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {customError} from '../../public_api'; -import {disabled, validate} from '../../src/api/logic'; +import {disabled, validate} from '../../src/api/rules'; import {applyEach, applyWhen, applyWhenValue, form, schema} from '../../src/api/structure'; import type {FieldTree, Schema} from '../../src/api/types'; @@ -19,11 +19,12 @@ interface TreeData { } function narrowed( - field: FieldTree | undefined, + fieldTree: FieldTree | undefined, guard: (value: TModel) => value is TNarrowed, ): Signal | undefined> { return computed( - () => field && (guard(field().value()) ? (field as FieldTree) : undefined), + () => + fieldTree && (guard(fieldTree().value()) ? (fieldTree as FieldTree) : undefined), ); } diff --git a/packages/forms/signals/test/node/resource.spec.ts b/packages/forms/signals/test/node/resource.spec.ts index 0bb4c5f0d2ba..6ed7cb8a5b68 100644 --- a/packages/forms/signals/test/node/resource.spec.ts +++ b/packages/forms/signals/test/node/resource.spec.ts @@ -7,12 +7,14 @@ */ import {provideHttpClient} from '@angular/common/http'; import {HttpTestingController, provideHttpClientTesting} from '@angular/common/http/testing'; -import {ApplicationRef, Injector, resource, signal} from '@angular/core'; +import {ApplicationRef, Injector, resource, signal, type Signal} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {isNode} from '@angular/private/testing'; import { applyEach, + applyWhen, + createManagedMetadataKey, customError, form, metadata, @@ -56,12 +58,13 @@ describe('resources', () => { it('Takes a simple resource which reacts to data changes', async () => { const s: SchemaOrSchemaFn = function (p) { - const RES = metadata(p.name, ({value}) => { - return resource({ - params: () => ({x: value()}), + const RES = createManagedMetadataKey((params: Signal<{x: string} | undefined>) => + resource({ + params, loader: async ({params}) => `got: ${params.x}`, - }); - }); + }), + ); + metadata(p.name, RES, ({value}) => ({x: value()})); validate(p.name, ({state}) => { const remote = state.metadata(RES)!; @@ -81,7 +84,7 @@ describe('resources', () => { expect(f.name().errors()).toEqual([ customError({ message: 'got: cat', - field: f.name, + fieldTree: f.name, }), ]); @@ -90,7 +93,7 @@ describe('resources', () => { expect(f.name().errors()).toEqual([ customError({ message: 'got: dog', - field: f.name, + fieldTree: f.name, }), ]); }); @@ -98,12 +101,13 @@ describe('resources', () => { it('should create a resource per entry in an array', async () => { const s: SchemaOrSchemaFn = function (p) { applyEach(p, (p) => { - const RES = metadata(p.name, ({value}) => { - return resource({ - params: () => ({x: value()}), + const RES = createManagedMetadataKey((params: Signal<{x: string} | undefined>) => + resource({ + params, loader: async ({params}) => `got: ${params.x}`, - }); - }); + }), + ); + metadata(p.name, RES, ({value}) => ({x: value()})); validate(p.name, ({state}) => { const remote = state.metadata(RES)!; @@ -124,13 +128,13 @@ describe('resources', () => { expect(f[0].name().errors()).toEqual([ customError({ message: 'got: cat', - field: f[0].name, + fieldTree: f[0].name, }), ]); expect(f[1].name().errors()).toEqual([ customError({ message: 'got: dog', - field: f[1].name, + fieldTree: f[1].name, }), ]); @@ -139,13 +143,13 @@ describe('resources', () => { expect(f[0].name().errors()).toEqual([ customError({ message: 'got: bunny', - field: f[0].name, + fieldTree: f[0].name, }), ]); expect(f[1].name().errors()).toEqual([ customError({ message: 'got: dog', - field: f[1].name, + fieldTree: f[1].name, }), ]); }); @@ -166,7 +170,7 @@ describe('resources', () => { customError({ kind: 'meows_too_much', name: cat.name, - field: fieldTreeOf(p)[index], + fieldTree: fieldTreeOf(p)[index], }), ); }, @@ -179,10 +183,10 @@ describe('resources', () => { await appRef.whenStable(); expect(f[0]().errors()).toEqual([ - customError({kind: 'meows_too_much', name: 'Fluffy', field: f[0]}), + customError({kind: 'meows_too_much', name: 'Fluffy', fieldTree: f[0]}), ]); expect(f[1]().errors()).toEqual([ - customError({kind: 'meows_too_much', name: 'Ziggy', field: f[1]}), + customError({kind: 'meows_too_much', name: 'Ziggy', fieldTree: f[1]}), ]); }); @@ -201,7 +205,7 @@ describe('resources', () => { return customError({ kind: 'meows_too_much', name: cats[0].name, - field: fieldTreeOf(p)[0], + fieldTree: fieldTreeOf(p)[0], }); }, onError: () => null, @@ -213,7 +217,7 @@ describe('resources', () => { await appRef.whenStable(); expect(f[0]().errors()).toEqual([ - customError({kind: 'meows_too_much', name: 'Fluffy', field: f[0]}), + customError({kind: 'meows_too_much', name: 'Fluffy', fieldTree: f[0]}), ]); expect(f[1]().errors()).toEqual([]); }); @@ -270,7 +274,7 @@ describe('resources', () => { validateHttp(address, { request: ({value}) => ({url: '/checkaddress', params: {...value()}}), onSuccess: (message: string, {fieldTreeOf}) => - customError({message, field: fieldTreeOf(address.street)}), + customError({message, fieldTree: fieldTreeOf(address.street)}), onError: () => null, }); }); @@ -287,9 +291,10 @@ describe('resources', () => { await appRef.whenStable(); expect(addressForm.street().errors()).toEqual([ - customError({message: 'Invalid!', field: addressForm.street}), + customError({message: 'Invalid!', fieldTree: addressForm.street}), ]); }); + it('should call onError handler when http validation fails', async () => { const addressModel = signal
    ({street: '123 Main St', city: '', zip: ''}); const addressSchema = schema
    ((address) => { @@ -298,13 +303,12 @@ describe('resources', () => { request: ({value}) => ({url: '/checkaddress', params: {...value()}}), onSuccess: () => undefined, onError: () => [ - customError({kind: 'address-api-failed', message: 'API is down', field: addressForm}), + customError({kind: 'address-api-failed', message: 'API is down', fieldTree: addressForm}), ], }); }); const addressForm = form(addressModel, addressSchema, {injector}); - TestBed.tick(); const req = backend.expectOne('/checkaddress?street=123%20Main%20St&city=&zip='); @@ -318,8 +322,70 @@ describe('resources', () => { customError({ kind: 'address-api-failed', message: 'API is down', - field: addressForm, + fieldTree: addressForm, }), ]); }); + + it('should allow double application of async validation schema with mutually exclusive predicate', async () => { + const toggle = signal(true); + const s = schema((p) => { + validateHttp(p, { + request: ({value}) => `/api/check?username=${value()}`, + onSuccess: (available: boolean) => + available ? undefined : customError({kind: 'username-taken'}), + onError: () => null, + }); + }); + const usernameForm = form( + signal('unique-user'), + (p) => { + applyWhen(p, () => toggle(), s); + applyWhen(p, () => !toggle(), s); + }, + {injector}, + ); + + TestBed.tick(); + const req1 = backend.expectOne('/api/check?username=unique-user'); + + expect(usernameForm().valid()).toBe(false); + expect(usernameForm().invalid()).toBe(false); + expect(usernameForm().pending()).toBe(true); + + req1.flush(true); + await appRef.whenStable(); + + expect(usernameForm().valid()).toBe(true); + expect(usernameForm().invalid()).toBe(false); + expect(usernameForm().pending()).toBe(false); + + toggle.update((v) => !v); + + // Toggling doesn't actually change the parameters, so we don't got back to pending. + expect(usernameForm().pending()).toBe(false); + + TestBed.tick(); + backend.expectNone('/api/check?username=unique-user'); + + usernameForm().value.set('new-user'); + + // Now that we've changed the parameters, go back to pending. + expect(usernameForm().pending()).toBe(true); + + TestBed.tick(); + const req3 = backend.expectOne('/api/check?username=new-user'); + req3.flush(true); + await appRef.whenStable(); + }); + + it('should not allow accessing resource metadata on a field that does not define its params', () => { + const RES = createManagedMetadataKey((params: Signal) => + resource({params, loader: async () => 'hi'}), + ); + + const f = form(signal(''), {injector: TestBed.inject(Injector)}); + + expect(f().metadata(RES)).toBe(undefined); + }); }); diff --git a/packages/forms/signals/test/node/submit.spec.ts b/packages/forms/signals/test/node/submit.spec.ts index c30867bdb2d4..3cdb794390d9 100644 --- a/packages/forms/signals/test/node/submit.spec.ts +++ b/packages/forms/signals/test/node/submit.spec.ts @@ -33,7 +33,7 @@ describe('submit', () => { fail('Submit action should run not on invalid form'); }); - expect(f.first().errors()).toEqual([requiredError({field: f.first})]); + expect(f.first().errors()).toEqual([requiredError({fieldTree: f.first})]); }); it('should not block on pending async validators', async () => { @@ -80,12 +80,12 @@ describe('submit', () => { return Promise.resolve( customError({ kind: 'lastName', - field: form.last, + fieldTree: form.last, }), ); }); - expect(f.last().errors()).toEqual([customError({kind: 'lastName', field: f.last})]); + expect(f.last().errors()).toEqual([customError({kind: 'lastName', fieldTree: f.last})]); }); it('maps errors to multiple fields', async () => { @@ -96,23 +96,23 @@ describe('submit', () => { return Promise.resolve([ customError({ kind: 'firstName', - field: form.first, + fieldTree: form.first, }), customError({ kind: 'lastName', - field: form.last, + fieldTree: form.last, }), customError({ kind: 'lastName2', - field: form.last, + fieldTree: form.last, }), ]); }); - expect(f.first().errors()).toEqual([customError({kind: 'firstName', field: f.first})]); + expect(f.first().errors()).toEqual([customError({kind: 'firstName', fieldTree: f.first})]); expect(f.last().errors()).toEqual([ - customError({kind: 'lastName', field: f.last}), - customError({kind: 'lastName2', field: f.last}), + customError({kind: 'lastName', fieldTree: f.last}), + customError({kind: 'lastName2', fieldTree: f.last}), ]); }); @@ -153,7 +153,7 @@ describe('submit', () => { return Promise.resolve(customError()); }); - expect(f().errors()).toEqual([customError({field: f})]); + expect(f().errors()).toEqual([customError({fieldTree: f})]); }); it('marks the form as submitting', async () => { @@ -259,18 +259,18 @@ describe('submit', () => { await submit(f, async (form) => { return [ - customError({kind: 'submit', field: f.first}), - customError({kind: 'submit', field: f.last}), + customError({kind: 'submit', fieldTree: f.first}), + customError({kind: 'submit', fieldTree: f.last}), ]; }); - expect(f.first().errors()).toEqual([customError({kind: 'submit', field: f.first})]); - expect(f.last().errors()).toEqual([customError({kind: 'submit', field: f.last})]); + expect(f.first().errors()).toEqual([customError({kind: 'submit', fieldTree: f.first})]); + expect(f.last().errors()).toEqual([customError({kind: 'submit', fieldTree: f.last})]); f.first().value.set('Hello'); expect(f.first().errors()).toEqual([]); - expect(f.last().errors()).toEqual([customError({kind: 'submit', field: f.last})]); + expect(f.last().errors()).toEqual([customError({kind: 'submit', fieldTree: f.last})]); }); }); diff --git a/packages/forms/signals/test/node/validation_status.spec.ts b/packages/forms/signals/test/node/validation_status.spec.ts index dc92903ea7ff..82bfe67fe7de 100644 --- a/packages/forms/signals/test/node/validation_status.spec.ts +++ b/packages/forms/signals/test/node/validation_status.spec.ts @@ -27,9 +27,9 @@ function validateValue(value: string): ValidationError[] { function validateValueForChild( value: string, - field: FieldTree | undefined, + fieldTree: FieldTree | undefined, ): ValidationError.WithField[] { - return value === 'INVALID' ? [customError({field})] : []; + return value === 'INVALID' ? [customError({fieldTree})] : []; } describe('validation status', () => { @@ -236,7 +236,7 @@ describe('validation status', () => { onSuccess: (results, {fieldTreeOf}) => results.map((e) => ({ ...e, - field: fieldTreeOf(p.child), + fieldTree: fieldTreeOf(p.child), })), onError: () => null, }); diff --git a/packages/forms/signals/test/web/compat_form.spec.ts b/packages/forms/signals/test/web/compat_form.spec.ts new file mode 100644 index 000000000000..a12d57b5b581 --- /dev/null +++ b/packages/forms/signals/test/web/compat_form.spec.ts @@ -0,0 +1,97 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component, provideZonelessChangeDetection, signal} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {TestBed} from '@angular/core/testing'; +import {Field} from '../../public_api'; +import {compatForm} from '../../compat'; + +describe('compatForm with [field] directive', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + }); + }); + + it('should bind compat form to input with [field] directive', () => { + @Component({ + imports: [Field], + template: ` + + + `, + }) + class TestCmp { + readonly cat = signal({ + name: new FormControl('pirojok-the-cat', {nonNullable: true}), + age: new FormControl(5, {nonNullable: true}), + }); + readonly f = compatForm(this.cat); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + const inputs = fixture.nativeElement.querySelectorAll('input'); + const nameInput = inputs[0] as HTMLInputElement; + const ageInput = inputs[1] as HTMLInputElement; + + expect(nameInput.value).toBe('pirojok-the-cat'); + expect(ageInput.value).toBe('5'); + }); + + it('should bind root-level FormControl to input with [field] directive', () => { + @Component({ + imports: [Field], + template: ``, + }) + class TestCmp { + readonly cat = signal(new FormControl('pirojok-the-cat', {nonNullable: true})); + readonly f = compatForm(this.cat); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + const input = fixture.nativeElement.querySelector('input') as HTMLInputElement; + + expect(input.value).toBe('pirojok-the-cat'); + }); + + it('should bind fields when FormControls are mixed with regular values', () => { + @Component({ + imports: [Field], + template: ` + + + `, + }) + class TestCmp { + readonly cat = signal({ + name: new FormControl('fluffy', {nonNullable: true}), + age: 3, + species: 'cat', + }); + readonly f = compatForm(this.cat); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + const inputs = fixture.nativeElement.querySelectorAll('input'); + const nameInput = inputs[0] as HTMLInputElement; + const ageInput = inputs[1] as HTMLInputElement; + + expect(nameInput.value).toBe('fluffy'); + expect(ageInput.value).toBe('3'); + expect(fixture.componentInstance.f().value().species).toBe('cat'); + }); +}); + +function act(fn: () => T): T { + try { + return fn(); + } finally { + TestBed.tick(); + } +} diff --git a/packages/forms/signals/test/web/dynamic_binding.spec.ts b/packages/forms/signals/test/web/dynamic_binding.spec.ts new file mode 100644 index 000000000000..0aa11d2e1cc3 --- /dev/null +++ b/packages/forms/signals/test/web/dynamic_binding.spec.ts @@ -0,0 +1,217 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { + Component, + createComponent, + EnvironmentInjector, + input, + inputBinding, + model, + signal, +} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; +import { + disabled, + Field, + FieldTree, + form, + FormCheckboxControl, + FormValueControl, + required, +} from '@angular/forms/signals'; + +describe('createComponent', () => { + describe('FormValueControl', () => { + it(`synchronizes value from '[field]' binding`, () => { + @Component({template: ''}) + class CustomInput implements FormValueControl { + readonly value = model.required(); + } + + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => form(signal('initial value'))); + + const fixture = createComponent(CustomInput, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }); + fixture.changeDetectorRef.detectChanges(); + + expect(control().fieldBindings()).toHaveSize(1); + expect(fixture.instance.value()).toBe('initial value'); + + // Model --> View + control().value.set('new value'); + fixture.changeDetectorRef.detectChanges(); + expect(fixture.instance.value()).toBe('new value'); + + // View --> Model + fixture.instance.value.set('from component'); + fixture.changeDetectorRef.detectChanges(); + expect(control().value()).toBe('from component'); + }); + + it(`synchronizes properties from '[field]' binding`, () => { + @Component({template: ''}) + class CustomInput implements FormValueControl { + readonly value = model.required(); + readonly disabled = model.required(); + } + + const disabledSignal = signal(false); + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => { + return form(signal('initial value'), (p) => { + disabled(p, disabledSignal); + }); + }); + + const fixture = createComponent(CustomInput, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }); + fixture.changeDetectorRef.detectChanges(); + + expect(control().fieldBindings()).toHaveSize(1); + expect(fixture.instance.disabled()).toBe(false); + + disabledSignal.set(true); + fixture.changeDetectorRef.detectChanges(); + expect(fixture.instance.disabled()).toBe(true); + }); + }); + + describe('FormCheckboxControl', () => { + it(`synchronizes value from '[field]' binding`, () => { + @Component({template: ''}) + class CustomCheckbox implements FormCheckboxControl { + readonly checked = model.required(); + } + + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => form(signal(true))); + + const fixture = createComponent(CustomCheckbox, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }); + fixture.changeDetectorRef.detectChanges(); + + expect(control().fieldBindings()).toHaveSize(1); + expect(fixture.instance.checked()).toBe(true); + + // Model --> View + control().value.set(false); + fixture.changeDetectorRef.detectChanges(); + expect(fixture.instance.checked()).toBe(false); + + // View --> Model + fixture.instance.checked.set(true); + fixture.changeDetectorRef.detectChanges(); + expect(control().value()).toBe(true); + }); + + it(`synchronizes properties from '[field]' binding`, () => { + @Component({template: ''}) + class CustomCheckbox implements FormCheckboxControl { + readonly checked = model.required(); + readonly required = model.required(); + } + + const requiredSignal = signal(false); + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => { + return form(signal(true), (p) => { + required(p, {when: requiredSignal}); + }); + }); + + const fixture = createComponent(CustomCheckbox, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }); + fixture.changeDetectorRef.detectChanges(); + + expect(control().fieldBindings()).toHaveSize(1); + expect(fixture.instance.required()).toBe(false); + + requiredSignal.set(true); + fixture.changeDetectorRef.detectChanges(); + expect(fixture.instance.required()).toBe(true); + }); + }); + + it(`should not treat component with '[field]' input as a control`, () => { + @Component({template: ''}) + class TestCmp { + readonly field = input.required>(); + readonly value = model.required(); + } + + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => { + return form(signal('initial value')); + }); + + const fixture = createComponent(TestCmp, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }); + fixture.changeDetectorRef.detectChanges(); + + expect(control().fieldBindings()).toHaveSize(0); + }); + + it(`should throw for invalid '[field]' binding host`, () => { + @Component({template: ''}) + class InvalidFieldHost {} + + const environmentInjector = TestBed.inject(EnvironmentInjector); + const control = TestBed.runInInjectionContext(() => { + return form(signal('initial value')); + }); + + expect(() => + createComponent(InvalidFieldHost, { + environmentInjector, + directives: [ + { + type: Field, + bindings: [inputBinding('field', () => control)], + }, + ], + }), + ).toThrowError(/Component InvalidFieldHost (.+) is an invalid \[field\] directive host\./); + }); +}); diff --git a/packages/forms/signals/test/web/field_directive.spec.ts b/packages/forms/signals/test/web/field_directive.spec.ts index 37d6f2c2e59a..73103c37ae3a 100644 --- a/packages/forms/signals/test/web/field_directive.spec.ts +++ b/packages/forms/signals/test/web/field_directive.spec.ts @@ -7,6 +7,7 @@ */ import { + booleanAttribute, Component, computed, Directive, @@ -16,6 +17,8 @@ import { input, inputBinding, model, + numberAttribute, + resource, signal, viewChild, viewChildren, @@ -37,6 +40,7 @@ import { provideSignalFormsConfig, readonly, required, + validateAsync, type DisabledReason, type FieldTree, type FormCheckboxControl, @@ -47,7 +51,7 @@ import { @Component({ selector: 'string-control', - template: ``, + template: ``, imports: [Field], }) class TestStringControl { @@ -60,7 +64,7 @@ describe('field directive', () => { it('should bind new field to control when changed', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly model = signal({x: 'a', y: 'b'}); @@ -80,7 +84,7 @@ describe('field directive', () => { it('should update new field when change value changes', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly model = signal({x: 'a', y: 'b'}); @@ -108,7 +112,7 @@ describe('field directive', () => { it('should bind to native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly disabled = signal(false); @@ -182,7 +186,7 @@ describe('field directive', () => { it('should be reset when field changes on native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal({x: 'a', y: 'b'}), (p) => { @@ -234,8 +238,8 @@ describe('field directive', () => { imports: [Field], template: ` @for (item of f; track item) { - - {{item().value()}} + + {{ item().value() }} } `, }) @@ -253,7 +257,7 @@ describe('field directive', () => { }); it('should bind to custom control', () => { - @Component({selector: 'custom-control', template: `{{value()}}`}) + @Component({selector: 'custom-control', template: `{{ value() }}`}) class CustomControl implements FormValueControl { readonly value = model(''); readonly name = input(''); @@ -310,7 +314,7 @@ describe('field directive', () => { it('should bind to native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly readonly = signal(true); @@ -383,7 +387,7 @@ describe('field directive', () => { it('should be reset when field changes on native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal({x: 'a', y: 'b'}), (p) => { @@ -433,7 +437,7 @@ describe('field directive', () => { it('should bind to native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly required = signal(false); @@ -506,7 +510,7 @@ describe('field directive', () => { it('should be reset when field changes on native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal({x: 'a', y: 'b'}), (p) => { @@ -556,7 +560,7 @@ describe('field directive', () => { it('should bind to native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly max = signal(10); @@ -629,7 +633,7 @@ describe('field directive', () => { it('should be reset when field changes on native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal({x: 1, y: 2}), (p) => { @@ -679,7 +683,7 @@ describe('field directive', () => { it('should bind to native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly min = signal(10); @@ -752,7 +756,7 @@ describe('field directive', () => { it('should be reset when field changes on native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal({x: 1, y: 2}), (p) => { @@ -1133,12 +1137,147 @@ describe('field directive', () => { }); }); + describe('input transforms', () => { + it('should accept InputSignal without transform', () => { + @Component({selector: 'custom-control', template: ``}) + class CustomControl implements FormValueControl { + readonly value = model(''); + readonly disabled = input(false); + readonly readonly = input(false); + readonly required = input(false); + readonly hidden = input(false); + readonly invalid = input(false); + readonly pending = input(false); + readonly dirty = input(false); + readonly touched = input(false); + readonly min = input(1); + readonly max = input(1_0000); + readonly minLength = input(1); + readonly maxLength = input(5); + } + + @Component({ + imports: [Field, CustomControl], + template: ``, + }) + class TestCmp { + readonly f = form(signal('')); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + expect(fixture.componentInstance).toBeDefined(); + }); + + it('should accept InputSignalWithTransform for boolean properties', () => { + @Component({selector: 'custom-control', template: ``}) + class CustomControl implements FormValueControl { + readonly value = model(''); + readonly disabled = input(false, {transform: booleanAttribute}); + readonly readonly = input(false, {transform: booleanAttribute}); + readonly required = input(false, {transform: booleanAttribute}); + readonly hidden = input(false, {transform: booleanAttribute}); + readonly invalid = input(false, {transform: booleanAttribute}); + readonly pending = input(false, {transform: booleanAttribute}); + readonly dirty = input(false, {transform: booleanAttribute}); + readonly touched = input(false, {transform: booleanAttribute}); + } + + @Component({ + imports: [Field, CustomControl], + template: ``, + }) + class TestCmp { + readonly f = form(signal('')); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + expect(fixture.componentInstance).toBeDefined(); + }); + + it('should accept InputSignalWithTransform for number properties', () => { + @Component({selector: 'custom-control', template: ``}) + class CustomControl implements FormValueControl { + readonly value = model(0); + readonly min = input(undefined, {transform: numberAttribute}); + readonly max = input(undefined, {transform: numberAttribute}); + readonly minLength = input(undefined, { + transform: numberAttribute, + }); + readonly maxLength = input(undefined, { + transform: numberAttribute, + }); + } + + @Component({ + imports: [Field, CustomControl], + template: ``, + }) + class TestCmp { + readonly f = form(signal(0)); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + expect(fixture.componentInstance).toBeDefined(); + }); + + it('should accept custom transform for arrays', () => { + @Component({selector: 'custom-control', template: ``}) + class CustomControl implements FormValueControl { + readonly value = model(''); + readonly name = input('', {transform: (v: unknown) => String(v ?? '')}); + readonly pattern = input([], { + transform: (v: unknown) => (Array.isArray(v) ? v : []), + }); + readonly errors = input[], unknown>([], { + transform: (v: unknown) => (Array.isArray(v) ? v : []), + }); + readonly disabledReasons = input[], unknown>( + [], + { + transform: (v: unknown) => (Array.isArray(v) ? v : []), + }, + ); + } + + @Component({ + imports: [Field, CustomControl], + template: ``, + }) + class TestCmp { + readonly f = form(signal('')); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + expect(fixture.componentInstance).toBeDefined(); + }); + + it('should accept mixed InputSignal and InputSignalWithTransform', () => { + @Component({selector: 'custom-control', template: ``}) + class CustomControl implements FormValueControl { + readonly value = model(''); + readonly disabled = input(false); + readonly readonly = input(false, {transform: booleanAttribute}); + readonly required = input(false); + readonly name = input('', {transform: (v: unknown) => String(v ?? '')}); + } + + @Component({ + imports: [Field, CustomControl], + template: ``, + }) + class TestCmp { + readonly f = form(signal('')); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + expect(fixture.componentInstance).toBeDefined(); + }); + }); + it('synchronizes with a value control', () => { @Component({ imports: [Field], - template: ` - - `, + template: ` `, }) class TestCmp { f = form(signal('test')); @@ -1166,7 +1305,7 @@ describe('field directive', () => { it('synchronizes with a checkbox control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal(false)); @@ -1442,8 +1581,8 @@ describe('field directive', () => { template: ` @if (!f().hidden()) { } @@ -1473,8 +1612,8 @@ describe('field directive', () => { template: ` @if (!f().hidden()) { } @@ -1503,8 +1642,8 @@ describe('field directive', () => { imports: [Field], template: ` `, @@ -1697,9 +1836,7 @@ describe('field directive', () => { } @Component({ - template: ` - - `, + template: ` `, imports: [CustomInput, Field], }) class ReadonlyTestCmp { @@ -1713,7 +1850,7 @@ describe('field directive', () => { const comp = act(() => TestBed.createComponent(ReadonlyTestCmp)).componentInstance; expect(comp.myInput().disabledReasons()).toEqual([ - {message: 'Currently unavailable', field: comp.f}, + {message: 'Currently unavailable', fieldTree: comp.f}, ]); }); @@ -1728,9 +1865,7 @@ describe('field directive', () => { } @Component({ - template: ` - - `, + template: ` `, imports: [CustomInput, Field], }) class ReadonlyTestCmp { @@ -1747,10 +1882,107 @@ describe('field directive', () => { expect(comp.myInput().invalid()).toBe(false); }); + it('should synchronize hidden status', () => { + @Component({ + selector: 'my-input', + template: '', + }) + class CustomInput implements FormValueControl { + value = model(''); + hidden = input(false); + } + + @Component({ + template: ` `, + imports: [CustomInput, Field], + }) + class HiddenTestCmp { + myInput = viewChild.required(CustomInput); + data = signal(''); + f = form(this.data, (p) => { + hidden(p, ({value}) => value() === ''); + }); + } + + const comp = act(() => TestBed.createComponent(HiddenTestCmp)).componentInstance; + expect(comp.myInput().hidden()).toBe(true); + act(() => comp.f().value.set('visible')); + expect(comp.myInput().hidden()).toBe(false); + }); + + it('should synchronize dirty status', () => { + @Component({ + selector: 'my-input', + template: '', + }) + class CustomInput implements FormValueControl { + value = model(''); + dirty = input(false); + } + + @Component({ + template: ` `, + imports: [CustomInput, Field], + }) + class DirtyTestCmp { + myInput = viewChild.required(CustomInput); + data = signal(''); + f = form(this.data); + } + + const comp = act(() => TestBed.createComponent(DirtyTestCmp)).componentInstance; + expect(comp.myInput().dirty()).toBe(false); + act(() => comp.f().markAsDirty()); + expect(comp.myInput().dirty()).toBe(true); + }); + + it('should synchronize pending status', async () => { + const {promise, resolve} = promiseWithResolvers(); + + @Component({ + selector: 'my-input', + template: '', + }) + class CustomInput implements FormValueControl { + value = model(''); + pending = input(false); + } + + @Component({ + template: ` `, + imports: [CustomInput, Field], + }) + class PendingTestCmp { + myInput = viewChild.required(CustomInput); + data = signal('test'); + f = form(this.data, (p) => { + validateAsync(p, { + params: () => [], + factory: (params) => + resource({ + params, + loader: () => promise, + }), + onSuccess: (results) => results, + onError: () => null, + }); + }); + } + + const fix = act(() => TestBed.createComponent(PendingTestCmp)); + + expect(fix.componentInstance.myInput().pending()).toBe(true); + + resolve([]); + await promise; + await fix.whenStable(); + expect(fix.componentInstance.myInput().pending()).toBe(false); + }); + it(`should mark field as touched on native control 'blur' event`, () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal('')); @@ -1816,10 +2048,10 @@ describe('field directive', () => { template: ` @for (reason of disabledReasons(); track $index) { -

    {{reason.message}}

    +

    {{ reason.message }}

    } @for (error of errors(); track $index) { -

    {{error.message}}

    +

    {{ error.message }}

    } `, }) @@ -1865,7 +2097,7 @@ describe('field directive', () => { @Component({ imports: [Field, InputDirective], - template: ``, + template: ``, }) class TestCmp { f = form(signal('test')); @@ -1894,7 +2126,7 @@ describe('field directive', () => { it('should sync string field with number type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal('123')); @@ -1922,7 +2154,7 @@ describe('field directive', () => { it('should sync number field with number type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal(123)); @@ -1950,7 +2182,7 @@ describe('field directive', () => { it('should sync string field with date type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal('2024-01-01')); @@ -1978,7 +2210,7 @@ describe('field directive', () => { it('should sync date field with date type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal(new Date('2024-01-01T12:00:00Z'))); @@ -2006,7 +2238,7 @@ describe('field directive', () => { it('should sync number field with date type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal(new Date('2024-01-01T12:00:00Z').valueOf())); @@ -2034,7 +2266,7 @@ describe('field directive', () => { it('should sync number field with datetime-local type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal(new Date('2024-01-01T12:30:00Z').valueOf())); @@ -2064,7 +2296,7 @@ describe('field directive', () => { it('should sync string field with color type input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal('#ff0000')); @@ -2092,7 +2324,7 @@ describe('field directive', () => { it('should sync string field with dynamically typed input', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly passwordVisible = signal(false); @@ -2139,7 +2371,7 @@ describe('field directive', () => { it('native control', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { f = form(signal('')); @@ -2201,7 +2433,7 @@ describe('field directive', () => { } expect(() => act(() => TestBed.createComponent(TestCmp))).toThrowError( - /'
    ' is an invalid \[field\] directive host\./, + /
    is an invalid \[field\] directive host\./, ); }); @@ -2210,10 +2442,10 @@ describe('field directive', () => { @Component({ imports: [Field], template: ` - @for (item of f; track item) { - - } - `, + @for (item of f; track item) { + + } + `, }) class TestCmp { readonly f = form(signal(['a', 'b']), {name: 'root'}); @@ -2255,10 +2487,10 @@ describe('field directive', () => { @Component({ imports: [Field], template: ` - @for (item of f; track item) { - - } - `, + @for (item of f; track item) { + + } + `, }) class TestCmp { readonly f = form(signal([{x: 'a'}, {x: 'b'}]), {name: 'root'}); @@ -2300,10 +2532,10 @@ describe('field directive', () => { @Component({ imports: [Field], template: ` - @for (item of f; track item) { - - } - `, + @for (item of f; track item) { + + } + `, }) class TestCmp { readonly f = form(signal([['a'], ['b']]), {name: 'root'}); @@ -2409,7 +2641,7 @@ describe('field directive', () => { providers: [ provideSignalFormsConfig({ classes: { - 'my-invalid-class': (state) => state.invalid(), + 'my-invalid-class': ({state}) => state().invalid(), }, }), ], @@ -2417,7 +2649,7 @@ describe('field directive', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal(''), (p) => { @@ -2444,7 +2676,7 @@ describe('field directive', () => { @Component({ imports: [Field], - template: ``, + template: ``, }) class TestCmp { readonly f = form(signal(''), (p) => { @@ -2526,6 +2758,76 @@ describe('field directive', () => { expect(customCtrl.classList.contains('always')).toBe(true); expect(customSubform.classList.contains('always')).toBe(false); }); + + it('should apply classes based on element', () => { + TestBed.configureTestingModule({ + providers: [ + provideSignalFormsConfig({ + classes: { + 'multiline': ({element}) => element.tagName.toLowerCase() === 'textarea', + }, + }), + ], + }); + + @Component({ + imports: [Field], + template: ` + + + `, + }) + class TestCmp { + readonly f = form(signal('')); + } + + const fixture = act(() => TestBed.createComponent(TestCmp)); + const input = fixture.nativeElement.querySelector('input'); + const textarea = fixture.nativeElement.querySelector('textarea'); + expect(input.classList.contains('multiline')).toBe(false); + expect(textarea.classList.contains('multiline')).toBe(true); + }); + }); + + it('should create & bind input when a macro task is running', async () => { + const {promise, resolve} = promiseWithResolvers(); + + @Component({ + selector: 'app-form', + imports: [Field], + template: ` + + + + `, + }) + class FormComponent { + form = form(signal('us')); + } + + @Component({ + selector: 'app-root', + template: ``, + }) + class App { + vcr = inject(ViewContainerRef); + constructor() { + promise.then(() => { + this.vcr.createComponent(FormComponent); + }); + } + } + + const fixture = act(() => TestBed.createComponent(App)); + + resolve(); + await fixture.whenStable(); + + const select = fixture.debugElement.parent!.nativeElement.querySelector('select'); + expect(select.value).toBe('us'); }); }); @@ -2534,9 +2836,9 @@ function setupRadioGroup() { imports: [Field], template: `
    - - - + + +
    `, }) diff --git a/packages/forms/signals/test/web/field_proxy.spec.ts b/packages/forms/signals/test/web/field_proxy.spec.ts index 64c8c8ddd6cd..19c50fe518b3 100644 --- a/packages/forms/signals/test/web/field_proxy.spec.ts +++ b/packages/forms/signals/test/web/field_proxy.spec.ts @@ -26,7 +26,9 @@ describe('field proxy', () => { it('@for over array field should be reactive', () => { @Component({ selector: 'iterate-field', - template: `@for (i of f(); track i) {

    hi

    }`, + template: `@for (i of f(); track i) { +

    hi

    + }`, changeDetection: ChangeDetectionStrategy.OnPush, }) class IterateFieldCmp { diff --git a/packages/forms/signals/test/web/interop.spec.ts b/packages/forms/signals/test/web/interop.spec.ts index 46272aea779a..05659e3edcf4 100644 --- a/packages/forms/signals/test/web/interop.spec.ts +++ b/packages/forms/signals/test/web/interop.spec.ts @@ -21,13 +21,13 @@ describe('ControlValueAccessor', () => { @Component({ selector: 'custom-control', template: ` - - `, + + `, providers: [{provide: NG_VALUE_ACCESSOR, useExisting: CustomControl, multi: true}], }) class CustomControl implements ControlValueAccessor { @@ -187,7 +187,7 @@ describe('ControlValueAccessor', () => { @Component({ selector: 'custom-control', template: ` - + `, }) class CustomControl implements ControlValueAccessor { diff --git a/packages/forms/src/directives/control_value_accessor.ts b/packages/forms/src/directives/control_value_accessor.ts index 2a5c16594a8e..27129fab868d 100644 --- a/packages/forms/src/directives/control_value_accessor.ts +++ b/packages/forms/src/directives/control_value_accessor.ts @@ -212,5 +212,5 @@ export class BuiltInControlValueAccessor extends BaseControlValueAccessor {} * @publicApi */ export const NG_VALUE_ACCESSOR = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'NgValueAccessor' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'NgValueAccessor' : '', ); diff --git a/packages/forms/src/directives/default_value_accessor.ts b/packages/forms/src/directives/default_value_accessor.ts index 4972a7c90a83..31638c1d2e1b 100644 --- a/packages/forms/src/directives/default_value_accessor.ts +++ b/packages/forms/src/directives/default_value_accessor.ts @@ -46,7 +46,7 @@ function _isAndroid(): boolean { * @publicApi */ export const COMPOSITION_BUFFER_MODE = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'CompositionEventMode' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'CompositionEventMode' : '', ); /** diff --git a/packages/forms/src/directives/reactive_directives/form_control_directive.ts b/packages/forms/src/directives/reactive_directives/form_control_directive.ts index 471efeaed191..4ce114d0a085 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_directive.ts @@ -42,7 +42,7 @@ import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../valid * Token to provide to turn off the ngModel warning on formControl and formControlName. */ export const NG_MODEL_WITH_FORM_CONTROL_WARNING = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'NgModelWithFormControlWarning' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'NgModelWithFormControlWarning' : '', ); const formControlBinding: Provider = { diff --git a/packages/forms/src/validators.ts b/packages/forms/src/validators.ts index 1470db16f0ff..8868376a20c7 100644 --- a/packages/forms/src/validators.ts +++ b/packages/forms/src/validators.ts @@ -79,7 +79,7 @@ function lengthOrSize(value: unknown): number | null { * @publicApi */ export const NG_VALIDATORS = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'NgValidators' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'NgValidators' : '', ); /** @@ -114,7 +114,7 @@ export const NG_VALIDATORS = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'NgAsyncValidators' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'NgAsyncValidators' : '', ); /** diff --git a/packages/forms/test/ng_control_status_spec.ts b/packages/forms/test/ng_control_status_spec.ts index d7ad58ad5bfd..16bf62663a58 100644 --- a/packages/forms/test/ng_control_status_spec.ts +++ b/packages/forms/test/ng_control_status_spec.ts @@ -18,7 +18,7 @@ describe('status host binding classes', () => { it('work in OnPush components', async () => { @Component({ selector: 'test-cmp', - template: ``, + template: ``, imports: [FormsModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts index cdd1c81b3333..bcacf0e43db8 100644 --- a/packages/forms/test/reactive_integration_spec.ts +++ b/packages/forms/test/reactive_integration_spec.ts @@ -375,10 +375,10 @@ describe('reactive forms integration tests', () => { it('should sync the disabled state if it changes right after a group is re-bound', () => { @Component({ template: ` -
    - -
    - `, +
    + +
    + `, standalone: false, }) class App { @@ -786,13 +786,13 @@ describe('reactive forms integration tests', () => { it('should handle FormControl and FormGroup swap', () => { @Component({ template: ` -
    - - - - -
    - `, +
    + + + + +
    + `, standalone: false, }) class App { @@ -846,13 +846,13 @@ describe('reactive forms integration tests', () => { it('should handle FormControl and FormArray swap', () => { @Component({ template: ` -
    - - - - -
    - `, +
    + + + + +
    + `, standalone: false, }) class App { @@ -906,15 +906,15 @@ describe('reactive forms integration tests', () => { it('should handle FormGroup and FormArray swap', () => { @Component({ template: ` -
    - - - - - - -
    - `, +
    + + + + + + +
    + `, standalone: false, }) class App { @@ -1500,12 +1500,11 @@ describe('reactive forms integration tests', () => { it('formArray should emit an event when resetting a form', () => { @Component({ selector: 'form-array-comp', - template: ` -
    - @for(_ of controls; track $index) { - - } -
    `, + template: `
    + @for (_ of controls; track $index) { + + } +
    `, standalone: false, }) class FormArrayComp { @@ -1628,9 +1627,7 @@ describe('reactive forms integration tests', () => { it('should not assign status on standalone
    element', () => { @Component({ selector: 'form-comp', - template: ` -
    - `, + template: `
    `, standalone: false, }) class FormComp {} @@ -1648,10 +1645,10 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'form-comp', template: ` -
    - -
    - `, +
    + +
    + `, standalone: false, }) class FormComp { @@ -2155,11 +2152,11 @@ describe('reactive forms integration tests', () => { it('should be able to remove a control as a result of another control being reset', () => { @Component({ template: ` -
    - - -
    - `, +
    + + +
    + `, standalone: false, }) class App implements OnDestroy { @@ -3537,9 +3534,15 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'min-max-length-null', template: ` -
    - -
    `, +
    + +
    + `, standalone: false, }) class MinMaxLengthComponent { @@ -3627,9 +3630,16 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'min-max-null', template: ` -
    - -
    `, +
    + +
    + `, standalone: false, }) class MinMaxComponent { @@ -4072,10 +4082,15 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'ng-model-noop-validation', template: ` -
    - + +
    - `, + `, standalone: false, }) class NgModelNoOpValidation { @@ -4821,8 +4836,8 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` - - `, + + `, standalone: false, }) class App { @@ -4898,9 +4913,9 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` - - - `, + + + `, standalone: false, }) class App { @@ -4972,11 +4987,11 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - -
    - `, +
    + + +
    + `, standalone: false, }) class App { @@ -5052,12 +5067,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` - -
    - -
    -
    - `, + +
    + +
    +
    + `, standalone: false, }) class App { @@ -5155,10 +5170,10 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - -
    - `, +
    + +
    + `, standalone: false, }) class App { @@ -5235,12 +5250,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -5347,12 +5362,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -5445,12 +5460,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -5568,12 +5583,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -5697,14 +5712,14 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - - +
    + + + -
    - `, +
    +
    + `, standalone: false, }) class App { @@ -5842,12 +5857,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -5938,12 +5953,12 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'app', template: ` -
    - - - -
    - `, +
    + + + +
    + `, standalone: false, }) class App { @@ -6013,10 +6028,10 @@ describe('reactive forms integration tests', () => { @Component({ selector: 'no-cva-compo', template: ` -
    -
    -
    - `, +
    +
    +
    + `, standalone: false, }) class NoCVAComponent { @@ -6040,12 +6055,11 @@ describe('reactive forms integration tests', () => { describe('formArray support', () => { @Component({ selector: 'form-array-comp', - template: ` -
    - @for(_ of controls; track $index) { - - } -
    `, + template: `
    + @for (_ of controls; track $index) { + + } +
    `, standalone: false, }) class FormArrayComp { @@ -6234,7 +6248,7 @@ class UniqLoginValidator implements AsyncValidator { @Component({ selector: 'form-control-comp', - template: ``, + template: ``, standalone: false, }) class FormControlComp { @@ -6243,10 +6257,9 @@ class FormControlComp { @Component({ selector: 'form-group-comp', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormGroupComp { @@ -6257,14 +6270,13 @@ class FormGroupComp { @Component({ selector: 'nested-form-group-name-comp', - template: ` -
    -
    - - -
    - -
    `, + template: `
    +
    + + +
    + +
    `, standalone: false, }) class NestedFormGroupNameComp { @@ -6273,14 +6285,13 @@ class NestedFormGroupNameComp { @Component({ selector: 'form-array-comp', - template: ` -
    -
    -
    - -
    + template: ` +
    +
    +
    - `, +
    + `, standalone: false, }) class FormArrayComp { @@ -6293,7 +6304,7 @@ class FormArrayComp { template: `
    - +
    `, @@ -6305,15 +6316,14 @@ class NestedFormArrayNameComp { @Component({ selector: 'form-array-nested-group', - template: ` -
    -
    -
    - - -
    + template: `
    +
    +
    + +
    -
    `, +
    +
    `, standalone: false, }) class FormArrayNestedGroup { @@ -6323,11 +6333,10 @@ class FormArrayNestedGroup { @Component({ selector: 'form-group-ng-model', - template: ` -
    - - -
    `, + template: `
    + + +
    `, standalone: false, }) class FormGroupNgModel { @@ -6339,8 +6348,8 @@ class FormGroupNgModel { @Component({ selector: 'form-control-ng-model', template: ` - - + + `, standalone: false, }) @@ -6353,13 +6362,12 @@ class FormControlNgModel { @Component({ selector: 'login-is-empty-wrapper', - template: ` -
    - - - - -
    `, + template: `
    + + + + +
    `, standalone: false, }) class LoginIsEmptyWrapper { @@ -6368,13 +6376,12 @@ class LoginIsEmptyWrapper { @Component({ selector: 'validation-bindings-form', - template: ` -
    - - - - -
    `, + template: `
    + + + + +
    `, standalone: false, }) class ValidationBindingsForm { @@ -6387,7 +6394,7 @@ class ValidationBindingsForm { @Component({ selector: 'form-control-checkbox-validator', - template: ``, + template: ``, standalone: false, }) class FormControlCheckboxRequiredValidator { @@ -6396,9 +6403,8 @@ class FormControlCheckboxRequiredValidator { @Component({ selector: 'uniq-login-wrapper', - template: ` -
    - + template: `
    +
    `, standalone: false, }) @@ -6410,7 +6416,7 @@ class UniqLoginWrapper { selector: 'form-group-with-validators', template: `
    - +
    `, standalone: false, @@ -6423,7 +6429,7 @@ class FormGroupWithValidators { selector: 'form-control-with-validators', template: `
    - +
    `, standalone: false, @@ -6443,7 +6449,7 @@ class FormControlWithAsyncValidatorFn { selector: 'form-control-with-validators', template: `
    - +
    `, standalone: false, @@ -6456,10 +6462,10 @@ class FormControlWithValidators { selector: 'ngfor-form-controls-with-validators', template: `
    - +
    - +
    `, standalone: false, @@ -6475,7 +6481,7 @@ class MultipleFormControls { template: `
    - +
    `, @@ -6488,10 +6494,9 @@ class NgForFormControlWithValidators { @Component({ selector: 'min-max-form-control-name', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class MinMaxFormControlNameComp { @@ -6503,10 +6508,9 @@ class MinMaxFormControlNameComp { @Component({ selector: 'min-max-form-control', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class MinMaxFormControlComp { @@ -6534,10 +6538,10 @@ class NativeDialogForm { @Component({ selector: 'radio-form', template: ` -
    - One - Two -
    +
    + One + Two +
    `, standalone: false, }) diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts index d39847b8b870..a8812283965e 100644 --- a/packages/forms/test/template_integration_spec.ts +++ b/packages/forms/test/template_integration_spec.ts @@ -88,7 +88,7 @@ describe('template-driven forms integration tests', () => { // https://github.com/angular/angular/issues/33695 @Component({ selector: 'app-root', - template: ``, + template: ``, standalone: false, }) class AppComponent { @@ -351,12 +351,12 @@ describe('template-driven forms integration tests', () => { it('should keep track of the ngModel value when together used with an ngFor inside a form', fakeAsync(() => { @Component({ template: ` -
    -
    - -
    -
    - `, +
    +
    + +
    +
    + `, standalone: false, }) class App { @@ -406,14 +406,14 @@ describe('template-driven forms integration tests', () => { it('should keep track of the ngModel value when together used with an ngFor inside an ngModelGroup', fakeAsync(() => { @Component({ template: ` -
    - -
    - -
    -
    -
    - `, +
    + +
    + +
    +
    +
    + `, standalone: false, }) class App { @@ -2089,10 +2089,10 @@ describe('template-driven forms integration tests', () => { @Component({ template: ` - - - - `, + + + + `, standalone: false, }) class AppComponent {} @@ -2574,12 +2574,17 @@ describe('template-driven forms integration tests', () => { @Component({ selector: 'ng-model-noop-validation', template: ` -
    -
    - -
    -
    - `, +
    +
    + +
    +
    + `, standalone: false, }) class NgModelNoOpValidation { @@ -2730,9 +2735,7 @@ describe('template-driven forms integration tests', () => { @Component({ selector: 'standalone-ng-model', - template: ` - - `, + template: ` `, standalone: false, }) class StandaloneNgModel { @@ -2742,8 +2745,8 @@ class StandaloneNgModel { @Component({ selector: 'ng-model-form', template: ` -
    - + +
    `, standalone: false, @@ -2768,10 +2771,10 @@ class NgModelNativeValidateForm {} template: `
    - - + +
    - +
    `, standalone: false, @@ -2789,7 +2792,7 @@ class NgModelGroupForm { template: `
    - + {{ group.valid }}
    @@ -2805,9 +2808,9 @@ class NgModelValidBinding { template: `
    - +
    - +
    `, standalone: false, @@ -2824,9 +2827,9 @@ class NgModelNgIfForm { template: `
    - +
    - +
    @@ -2842,7 +2845,7 @@ class NgModelNestedForm { selector: 'ng-no-form', template: `
    - +
    `, standalone: false, @@ -2853,7 +2856,7 @@ class NgNoFormComp {} selector: 'invalid-ng-model-noname', template: `
    - +
    `, standalone: false, @@ -2864,8 +2867,8 @@ class InvalidNgModelNoName {} selector: 'ng-model-options-standalone', template: `
    - - + +
    `, standalone: false, @@ -2881,10 +2884,10 @@ class NgModelOptionsStandalone { selector: 'ng-model-validation-bindings', template: `
    - - - - + + + +
    `, standalone: false, @@ -2900,7 +2903,13 @@ class NgModelValidationBindings { selector: 'ng-model-multiple-validators', template: `
    - +
    `, standalone: false, @@ -2913,7 +2922,9 @@ class NgModelMultipleValidators { @Component({ selector: 'ng-model-checkbox-validator', - template: `
    `, + template: `
    + +
    `, standalone: false, }) class NgModelCheckboxRequiredValidator { @@ -2923,7 +2934,7 @@ class NgModelCheckboxRequiredValidator { @Component({ selector: 'ng-model-email', - template: `
    `, + template: `
    `, standalone: false, }) class NgModelEmailValidator { @@ -2945,7 +2956,7 @@ class NgAsyncValidator implements AsyncValidator { @Component({ selector: 'ng-model-async-validation', - template: ``, + template: ``, standalone: false, }) class NgModelAsyncValidation {} @@ -2954,8 +2965,7 @@ class NgModelAsyncValidation {} selector: 'ng-model-changes-form', template: `
    - +
    `, standalone: false, @@ -2973,8 +2983,7 @@ class NgModelChangesForm { @Component({ selector: 'ng-model-change-state', template: ` - + `, standalone: false, }) @@ -2984,7 +2993,7 @@ class NgModelChangeState { @Component({ selector: 'ng-model-max', - template: `
    `, + template: `
    `, standalone: false, }) class NgModelMaxValidator { @@ -2993,7 +3002,7 @@ class NgModelMaxValidator { @Component({ selector: 'ng-model-min', - template: `
    `, + template: `
    `, standalone: false, }) class NgModelMinValidator { @@ -3002,8 +3011,7 @@ class NgModelMinValidator { @Component({ selector: 'ng-model-min-max', - template: ` -
    `, + template: `
    `, standalone: false, }) class NgModelMinMaxValidator { @@ -3024,8 +3032,8 @@ class CustomDirective { selector: 'ng-model-no-min-max', template: `
    - - + +
    `, standalone: false, diff --git a/packages/forms/test/value_accessor_integration_spec.ts b/packages/forms/test/value_accessor_integration_spec.ts index a55c0777c136..4bbdce8f386e 100644 --- a/packages/forms/test/value_accessor_integration_spec.ts +++ b/packages/forms/test/value_accessor_integration_spec.ts @@ -1712,7 +1712,7 @@ describe('value accessors in reactive forms with custom options', () => { @Component({ selector: 'form-control-comp', - template: ``, + template: ``, standalone: false, }) export class FormControlComp { @@ -1721,10 +1721,9 @@ export class FormControlComp { @Component({ selector: 'form-group-comp', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) export class FormGroupComp { @@ -1736,7 +1735,7 @@ export class FormGroupComp { @Component({ selector: 'form-control-number-input', - template: ``, + template: ``, standalone: false, }) class FormControlNumberInput { @@ -1745,12 +1744,11 @@ class FormControlNumberInput { @Component({ selector: 'form-control-name-select', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlNameSelect { @@ -1760,12 +1758,11 @@ class FormControlNameSelect { @Component({ selector: 'form-control-select-ngValue', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectNgValue { @@ -1778,12 +1775,11 @@ class FormControlSelectNgValue { @Component({ selector: 'form-control-select-compare-with', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectWithCompareFn { @@ -1798,12 +1794,11 @@ class FormControlSelectWithCompareFn { @Component({ selector: 'form-control-select-compare-with-perf', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectWithComparePerfFn { @@ -1826,12 +1821,11 @@ class FormControlSelectWithComparePerfFn { @Component({ selector: 'form-control-select-compare-with-track-by', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectWithCompareTrackByFn { @@ -1847,12 +1841,11 @@ class FormControlSelectWithCompareTrackByFn { @Component({ selector: 'form-control-select-multiple', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectMultiple { @@ -1862,12 +1855,11 @@ class FormControlSelectMultiple { @Component({ selector: 'form-control-select-multiple', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectMultipleNgValue { @@ -1880,12 +1872,11 @@ class FormControlSelectMultipleNgValue { @Component({ selector: 'form-control-select-multiple-compare-with', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class FormControlSelectMultipleWithCompareFn { @@ -1902,7 +1893,7 @@ class FormControlSelectMultipleWithCompareFn { selector: 'ng-model-select-form', template: ` `, standalone: false, @@ -1918,7 +1909,7 @@ class NgModelSelectForm {
    `, @@ -1932,7 +1923,7 @@ class NgModelSelectWithPlaceholderForm { selector: 'ng-model-select-null-form', template: ` `, @@ -1947,7 +1938,7 @@ class NgModelSelectWithNullForm { selector: 'ng-model-select-compare-with', template: ` `, standalone: false, @@ -1963,7 +1954,7 @@ class NgModelSelectWithCustomCompareFnForm { selector: 'ng-model-select-multiple-compare-with', template: ` `, standalone: false, @@ -1979,7 +1970,7 @@ class NgModelSelectMultipleWithCustomCompareFnForm { selector: 'ng-model-select-multiple-form', template: ` `, standalone: false, @@ -1991,7 +1982,7 @@ class NgModelSelectMultipleForm { @Component({ selector: 'form-control-range-input', - template: ``, + template: ``, standalone: false, }) class FormControlRangeInput { @@ -2009,15 +2000,14 @@ class NgModelRangeForm { @Component({ selector: 'form-control-radio-buttons', - template: ` -
    - - - - + template: ` + + + +
    - - `, + + `, standalone: false, }) export class FormControlRadioButtons { @@ -2029,11 +2019,11 @@ export class FormControlRadioButtons { selector: 'ng-model-radio-form', template: `
    - - + + - - + +
    `, standalone: false, @@ -2077,7 +2067,7 @@ class WrappedValue implements ControlValueAccessor { @Component({ selector: 'cva-with-disabled-state', template: ` -
    CALLED WITH {{disabled ? 'DISABLED' : 'ENABLED'}}
    +
    CALLED WITH {{ disabled ? 'DISABLED' : 'ENABLED' }}
    UNSET
    `, providers: [{provide: NG_VALUE_ACCESSOR, multi: true, useExisting: CvaWithDisabledState}], @@ -2099,10 +2089,9 @@ class CvaWithDisabledState implements ControlValueAccessor { @Component({ selector: 'wrapped-value-form', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class CvaWithDisabledStateForm { @@ -2145,10 +2134,9 @@ export class MyInput implements ControlValueAccessor { @Component({ selector: 'my-input-form', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) export class MyInputForm { @@ -2158,10 +2146,9 @@ export class MyInputForm { @Component({ selector: 'wrapped-value-form', - template: ` -
    - -
    `, + template: `
    + +
    `, standalone: false, }) class WrappedValueForm { @@ -2171,7 +2158,12 @@ class WrappedValueForm { @Component({ selector: 'ng-model-custom-comp', template: ` - + `, providers: [{provide: NG_VALUE_ACCESSOR, multi: true, useExisting: NgModelCustomComp}], standalone: false, @@ -2200,7 +2192,11 @@ export class NgModelCustomComp implements ControlValueAccessor { selector: 'ng-model-custom-wrapper', template: `
    - +
    `, standalone: false, diff --git a/packages/language-service/build.sh b/packages/language-service/build.sh index e63b0de8f396..62abdc7ba6b2 100755 --- a/packages/language-service/build.sh +++ b/packages/language-service/build.sh @@ -5,7 +5,9 @@ set -ex # so that it can be consumed by the Angular extension for local development. # Usage: ./build.sh /path/to/vscode-ng-language-service -readonly bazel_bin=$(pnpm --silent bazel info bazel-bin) +# TODO: Remove --ignore_all_rc_files flag once a repository can be loaded in bazelrc during info +# commands again. See https://github.com/bazelbuild/bazel/issues/25145 for more context. +readonly bazel_bin=$(pnpm --silent bazel --ignore_all_rc_files info bazel-bin) readonly extension_repo="$1" if [[ -z "${extension_repo}" ]]; then diff --git a/packages/language-service/src/language_service.ts b/packages/language-service/src/language_service.ts index 7bafe1eabdd8..da582ba1670c 100644 --- a/packages/language-service/src/language_service.ts +++ b/packages/language-service/src/language_service.ts @@ -9,7 +9,11 @@ import {AST, TmplAstNode} from '@angular/compiler'; import {CompilerOptions, ConfigurationHost, readConfiguration} from '@angular/compiler-cli'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; -import {ErrorCode, ngErrorCode} from '@angular/compiler-cli/src/ngtsc/diagnostics'; +import { + ErrorCode, + isFatalDiagnosticError, + ngErrorCode, +} from '@angular/compiler-cli/src/ngtsc/diagnostics'; import {absoluteFrom, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; import {PerfPhase} from '@angular/compiler-cli/src/ngtsc/perf'; import {FileUpdate, ProgramDriver} from '@angular/compiler-cli/src/ngtsc/program_driver'; @@ -141,11 +145,20 @@ export class LanguageService { const components = compiler.getComponentsWithTemplateFile(fileName); for (const component of components) { if (ts.isClassDeclaration(component)) { - diagnostics.push( - ...compiler - .getTemplateTypeChecker() - .getSuggestionDiagnosticsForComponent(component, this.tsLS), - ); + try { + diagnostics.push( + ...compiler + .getTemplateTypeChecker() + .getSuggestionDiagnosticsForComponent(component, this.tsLS), + ); + } catch (e) { + // Type check code may throw fatal diagnostic errors if e.g. the type check + // block cannot be generated. In this case, we consider that there are no available suggestion diagnostics. + if (isFatalDiagnosticError(e)) { + continue; + } + throw e; + } } } } diff --git a/packages/language-service/src/quick_info_built_ins.ts b/packages/language-service/src/quick_info_built_ins.ts index 91d72be26fac..6ebc8cbf51b9 100644 --- a/packages/language-service/src/quick_info_built_ins.ts +++ b/packages/language-service/src/quick_info_built_ins.ts @@ -210,8 +210,7 @@ const BUILT_IN_NAMES_TO_DOC_MAP: { 'hydrate': { docString: "Keyword that indicates when the block's content will be hydrated. You can use `on` and `when` conditions as hydration triggers, or `hydrate never` to disable hydration for this block.", - // TODO(crisbeto): add link to partial hydration guide - links: [], + links: ['[Reference](https://angular.dev/guide/incremental-hydration)'], displayInfoKind: DisplayInfoKind.KEYWORD, }, 'when': { diff --git a/packages/language-service/test/legacy/project/app/app.component.ts b/packages/language-service/test/legacy/project/app/app.component.ts index 6fcd133fb87b..46bf36ce29f1 100644 --- a/packages/language-service/test/legacy/project/app/app.component.ts +++ b/packages/language-service/test/legacy/project/app/app.component.ts @@ -22,8 +22,8 @@ export interface Hero { @Component({ selector: 'my-app', template: ` -

    {{title}}

    -

    {{hero.name}} details!

    +

    {{ title }}

    +

    {{ hero.name }} details!

    `, standalone: false, }) diff --git a/packages/localize/PACKAGE.md b/packages/localize/PACKAGE.md index 60d711704581..9e5375a72cb4 100644 --- a/packages/localize/PACKAGE.md +++ b/packages/localize/PACKAGE.md @@ -26,7 +26,7 @@ warning = $localize`${this.process} is not right`; could be replaced with: ```ts -warning = "" + this.process + ", n'est pas bon."; +warning = '' + this.process + ", n'est pas bon."; ``` The result is that all references to `$localize` are removed, and there is **zero runtime cost** to rendering @@ -44,7 +44,7 @@ For example, the following template: would be compiled to something like: ```ts -ɵɵelementStart(0, "h1"); //

    +ɵɵelementStart(0, 'h1'); //

    ɵɵi18n(1, $localize`Hello, World!`); // Hello, World! ɵɵelementEnd(); //

    ``` diff --git a/packages/localize/init/BUILD.bazel b/packages/localize/init/BUILD.bazel index 657c825bf726..e14d7146460d 100644 --- a/packages/localize/init/BUILD.bazel +++ b/packages/localize/init/BUILD.bazel @@ -1,4 +1,3 @@ -load("//adev/shared-docs/pipeline/api-gen:generate_api_docs.bzl", "generate_api_docs") load("//tools:defaults.bzl", "ts_project") package(default_visibility = ["//visibility:public"]) @@ -24,13 +23,3 @@ filegroup( "*.ts", ]) + ["PACKAGE.md"], ) - -generate_api_docs( - name = "localize_docs", - srcs = [ - ":files_for_docgen", - "//packages:common_files_and_deps_for_docs", - ], - entry_point = ":index.ts", - module_name = "@angular/localize/init", -) diff --git a/packages/misc/angular-in-memory-web-api/README.md b/packages/misc/angular-in-memory-web-api/README.md index b5c2b80cc479..2ba1c3bf6ac0 100644 --- a/packages/misc/angular-in-memory-web-api/README.md +++ b/packages/misc/angular-in-memory-web-api/README.md @@ -84,15 +84,15 @@ and whose values are arrays of collection objects to return or update. For example: ```ts -import { InMemoryDbService } from 'angular-in-memory-web-api'; +import {InMemoryDbService} from 'angular-in-memory-web-api'; export class InMemHeroService implements InMemoryDbService { createDb() { let heroes = [ - { id: 1, name: 'Windstorm' }, - { id: 2, name: 'Bombasto' }, - { id: 3, name: 'Magneta' }, - { id: 4, name: 'Tornado' } + {id: 1, name: 'Windstorm'}, + {id: 2, name: 'Bombasto'}, + {id: 3, name: 'Magneta'}, + {id: 4, name: 'Tornado'}, ]; return {heroes}; } diff --git a/packages/platform-browser/animations/async/src/async_animation_renderer.ts b/packages/platform-browser/animations/async/src/async_animation_renderer.ts index daec1d0ac8b8..8e2d36c68164 100644 --- a/packages/platform-browser/animations/async/src/async_animation_renderer.ts +++ b/packages/platform-browser/animations/async/src/async_animation_renderer.ts @@ -324,5 +324,5 @@ export class DynamicDelegationRenderer implements Renderer2 { * Private token for investigation purposes */ export const ɵASYNC_ANIMATION_LOADING_SCHEDULER_FN = new InjectionToken<(loadFn: () => T) => T>( - typeof ngDevMode !== undefined && ngDevMode ? 'async_animation_loading_scheduler_fn' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'async_animation_loading_scheduler_fn' : '', ); diff --git a/packages/platform-browser/animations/async/test/animation_renderer_spec.ts b/packages/platform-browser/animations/async/test/animation_renderer_spec.ts index 6a5415b3c7eb..93011859ec33 100644 --- a/packages/platform-browser/animations/async/test/animation_renderer_spec.ts +++ b/packages/platform-browser/animations/async/test/animation_renderer_spec.ts @@ -343,10 +343,10 @@ type AnimationBrowserModule = typeof import('@angular/animations/browser'); @Component({ selector: 'my-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('animation1', [transition('a => b', [])]), trigger('animation2', [transition(':leave', [])]), diff --git a/packages/platform-browser/animations/test/animation_renderer_spec.ts b/packages/platform-browser/animations/test/animation_renderer_spec.ts index 13aea7ad028d..7481648f2cb0 100644 --- a/packages/platform-browser/animations/test/animation_renderer_spec.ts +++ b/packages/platform-browser/animations/test/animation_renderer_spec.ts @@ -250,10 +250,10 @@ import {withBody, isNode, el} from '@angular/private/testing'; @Component({ selector: 'my-cmp', template: ` -
    -
    -
    - `, +
    +
    +
    + `, animations: [ trigger('animation1', [transition('a => b', [])]), trigger('animation2', [transition(':leave', [])]), @@ -338,9 +338,7 @@ import {withBody, isNode, el} from '@angular/private/testing'; it('should provide hooks at the start and end of change detection', () => { @Component({ selector: 'my-cmp', - template: ` -
    - `, + template: `
    `, animations: [trigger('myAnimation', [])], standalone: false, }) diff --git a/packages/platform-browser/src/dom/dom_renderer.ts b/packages/platform-browser/src/dom/dom_renderer.ts index d983439ecacc..87b51bc0e260 100644 --- a/packages/platform-browser/src/dom/dom_renderer.ts +++ b/packages/platform-browser/src/dom/dom_renderer.ts @@ -63,7 +63,7 @@ const REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT = true; * @publicApi */ export const REMOVE_STYLES_ON_COMPONENT_DESTROY = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'RemoveStylesOnCompDestroy' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'RemoveStylesOnCompDestroy' : '', { factory: () => REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT, }, @@ -134,7 +134,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { EmulatedEncapsulationDomRenderer2 | NoneEncapsulationDomRenderer >(); private readonly defaultRenderer: Renderer2; - private readonly platformIsServer: boolean; constructor( private readonly eventManager: EventManager, @@ -148,14 +147,7 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { @Optional() private readonly tracingService: TracingService | null = null, ) { - this.platformIsServer = typeof ngServerMode !== 'undefined' && ngServerMode; - this.defaultRenderer = new DefaultDomRenderer2( - eventManager, - doc, - ngZone, - this.platformIsServer, - this.tracingService, - ); + this.defaultRenderer = new DefaultDomRenderer2(eventManager, doc, ngZone, this.tracingService); } createRenderer(element: any, type: RendererType2 | null): Renderer2 { @@ -195,7 +187,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { const eventManager = this.eventManager; const sharedStylesHost = this.sharedStylesHost; const removeStylesOnCompDestroy = this.removeStylesOnCompDestroy; - const platformIsServer = this.platformIsServer; const tracingService = this.tracingService; switch (type.encapsulation) { @@ -208,7 +199,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { removeStylesOnCompDestroy, doc, ngZone, - platformIsServer, tracingService, ); break; @@ -220,7 +210,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { doc, ngZone, this.nonce, - platformIsServer, tracingService, sharedStylesHost, ); @@ -232,7 +221,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { doc, ngZone, this.nonce, - platformIsServer, tracingService, ); @@ -244,7 +232,6 @@ export class DomRendererFactory2 implements RendererFactory2, OnDestroy { removeStylesOnCompDestroy, doc, ngZone, - platformIsServer, tracingService, ); break; @@ -282,7 +269,6 @@ class DefaultDomRenderer2 implements Renderer2 { private readonly eventManager: EventManager, private readonly doc: Document, protected readonly ngZone: NgZone, - private readonly platformIsServer: boolean, private readonly tracingService: TracingService | null, ) {} @@ -514,11 +500,10 @@ class ShadowDomRenderer extends DefaultDomRenderer2 { doc: Document, ngZone: NgZone, nonce: string | null, - platformIsServer: boolean, tracingService: TracingService | null, private sharedStylesHost?: SharedStylesHost, ) { - super(eventManager, doc, ngZone, platformIsServer, tracingService); + super(eventManager, doc, ngZone, tracingService); this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'}); // SharedStylesHost is used to add styles to the shadow root by ShadowDom. @@ -602,11 +587,10 @@ class NoneEncapsulationDomRenderer extends DefaultDomRenderer2 { private removeStylesOnCompDestroy: boolean, doc: Document, ngZone: NgZone, - platformIsServer: boolean, tracingService: TracingService | null, compId?: string, ) { - super(eventManager, doc, ngZone, platformIsServer, tracingService); + super(eventManager, doc, ngZone, tracingService); let styles = component.styles; if (ngDevMode) { // We only do this in development, as for production users should not add CSS sourcemaps to components. @@ -644,7 +628,6 @@ class EmulatedEncapsulationDomRenderer2 extends NoneEncapsulationDomRenderer { removeStylesOnCompDestroy: boolean, doc: Document, ngZone: NgZone, - platformIsServer: boolean, tracingService: TracingService | null, ) { const compId = appId + '-' + component.id; @@ -655,7 +638,6 @@ class EmulatedEncapsulationDomRenderer2 extends NoneEncapsulationDomRenderer { removeStylesOnCompDestroy, doc, ngZone, - platformIsServer, tracingService, compId, ); diff --git a/packages/platform-browser/src/dom/events/event_manager.ts b/packages/platform-browser/src/dom/events/event_manager.ts index 5aa464bdc0e4..c0873039d631 100644 --- a/packages/platform-browser/src/dom/events/event_manager.ts +++ b/packages/platform-browser/src/dom/events/event_manager.ts @@ -29,7 +29,7 @@ import {DomEventsPlugin} from './dom_events'; * @publicApi */ export const EVENT_MANAGER_PLUGINS = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'EventManagerPlugins' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'EventManagerPlugins' : '', ); /** diff --git a/packages/platform-browser/src/hydration.ts b/packages/platform-browser/src/hydration.ts index 1cfd4fabeb48..9b7c6a03ca12 100644 --- a/packages/platform-browser/src/hydration.ts +++ b/packages/platform-browser/src/hydration.ts @@ -22,6 +22,7 @@ import { ɵZONELESS_ENABLED as ZONELESS_ENABLED, ɵwithIncrementalHydration, ɵIS_ENABLED_BLOCKING_INITIAL_NAVIGATION as IS_ENABLED_BLOCKING_INITIAL_NAVIGATION, + provideStabilityDebugging, } from '@angular/core'; import {RuntimeErrorCode} from './errors'; @@ -255,6 +256,7 @@ export function provideClientHydration( typeof ngDevMode !== 'undefined' && ngDevMode ? provideEnabledBlockingInitialNavigationDetector() : [], + typeof ngDevMode !== 'undefined' && ngDevMode ? provideStabilityDebugging() : [], withDomHydration(), featuresKind.has(HydrationFeatureKind.NoHttpTransferCache) || hasHttpTransferCacheOptions ? [] diff --git a/packages/platform-browser/test/browser/bootstrap_spec.ts b/packages/platform-browser/test/browser/bootstrap_spec.ts index f3327d0730b1..af9ceb1a8567 100644 --- a/packages/platform-browser/test/browser/bootstrap_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_spec.ts @@ -51,7 +51,7 @@ import {ɵLog as Log, inject, TestBed} from '@angular/core/testing'; import {BrowserModule} from '../../index'; import {provideAnimations, provideNoopAnimations} from '../../animations'; import {expect} from '@angular/private/testing/matchers'; -import {isNode} from '@angular/private/testing'; +import {isNode, withBody} from '@angular/private/testing'; import {bootstrapApplication, platformBrowser} from '../../src/browser'; @@ -920,28 +920,37 @@ describe('providePlatformInitializer', () => { createPlatformInjector([ {provide: TEST_TOKEN, useValue: 'test'}, - providePlatformInitializer(() => { - injectedValue = _inject(TEST_TOKEN); - }), + providePlatformInitializer(() => (injectedValue = _inject(TEST_TOKEN))), ]); expect(injectedValue).toBe('test'); }); - function createPlatformInjector(providers: Array) { - /* TODO: should we change `createOrReusePlatformInjector` type to allow `EnvironmentProviders`? - */ - return createOrReusePlatformInjector(providers as any); + function createPlatformInjector(providers: Array) { + return createOrReusePlatformInjector(providers); } -}); -/** - * Typing tests. - */ -@Component({ - template: '', - // @ts-expect-error: `providePlatformInitializer()` should not work with Component.providers, as - // it wouldn't be executed anyway. - providers: [providePlatformInitializer(() => {})], -}) -class Test {} + it('should bootstrap with platform initializers', async () => { + return withBody('', async () => { + @Component({ + selector: 'app', + template: '', + }) + class App {} + + let platformInitializerCalls = 0; + + const platformRef = platformBrowser([ + providePlatformInitializer(() => { + platformInitializerCalls++; + }), + ]); + + expect(platformInitializerCalls).toBe(0); + await bootstrapApplication(App, undefined, {platformRef}); + expect(platformInitializerCalls).toBe(1); + await bootstrapApplication(App, undefined, {platformRef}); + expect(platformInitializerCalls).toBe(1); + }); + }); +}); diff --git a/packages/platform-browser/test/browser/bootstrap_standalone_spec.ts b/packages/platform-browser/test/browser/bootstrap_standalone_spec.ts index e46eb9dbd4a5..9ab39fc65e37 100644 --- a/packages/platform-browser/test/browser/bootstrap_standalone_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_standalone_spec.ts @@ -47,7 +47,7 @@ describe('bootstrapApplication for standalone components', () => { @Component({ selector: 'test-app', - template: `({{testToken}})`, + template: `({{ testToken }})`, imports: [AmbientModule], }) class StandaloneCmp { @@ -112,7 +112,7 @@ describe('bootstrapApplication for standalone components', () => { @Component({ selector: 'test-app', - template: `({{service.ambientToken}})`, + template: `({{ service.ambientToken }})`, imports: [AmbientModule], }) class StandaloneCmp { diff --git a/packages/platform-browser/test/dom/dom_renderer_spec.ts b/packages/platform-browser/test/dom/dom_renderer_spec.ts index 4882890dc739..5d00e88e231f 100644 --- a/packages/platform-browser/test/dom/dom_renderer_spec.ts +++ b/packages/platform-browser/test/dom/dom_renderer_spec.ts @@ -431,13 +431,14 @@ async function styleCount( @Component({ selector: 'cmp-emulated', - template: ` -
    `, + template: `
    `, styles: [ - `.emulated { - background-color: blue; - color: blue; - }`, + ` + .emulated { + background-color: blue; + color: blue; + } + `, ], encapsulation: ViewEncapsulation.Emulated, standalone: false, @@ -446,13 +447,14 @@ class CmpEncapsulationEmulated {} @Component({ selector: 'cmp-none', - template: ` -
    `, + template: `
    `, styles: [ - `.none { - background-color: lime; - color: lime; - }`, + ` + .none { + background-color: lime; + color: lime; + } + `, ], encapsulation: ViewEncapsulation.None, standalone: false, @@ -461,15 +463,16 @@ class CmpEncapsulationNone {} @Component({ selector: 'cmp-none', - template: ` -
    `, + template: `
    `, styles: [ - `.none { - background-color: lime; - color: lime; - } + ` + .none { + background-color: lime; + color: lime; + } - /*# sourceMappingURL=cmp-none.css.map */`, + /*# sourceMappingURL=cmp-none.css.map */ + `, ], encapsulation: ViewEncapsulation.None, standalone: false, @@ -478,12 +481,13 @@ class CmpEncapsulationNoneWithSourceMap {} @Component({ selector: 'cmp-shadow', - template: ` -
    `, + template: `
    `, styles: [ - `.shadow { - color: red; - }`, + ` + .shadow { + color: red; + } + `, ], encapsulation: ViewEncapsulation.ShadowDom, standalone: false, @@ -492,15 +496,16 @@ class CmpEncapsulationShadow {} @Component({ selector: 'cmp-shadow-children', - template: ` -
    - - -
    `, + template: `
    + + +
    `, styles: [ - `.shadow { - color: red; - }`, + ` + .shadow { + color: red; + } + `, ], encapsulation: ViewEncapsulation.ExperimentalIsolatedShadowDom, standalone: false, @@ -520,9 +525,7 @@ export class SomeApp {} @Component({ selector: 'shadow-parent-app-with-children', - template: ` - - `, + template: ` `, standalone: false, }) export class IsolatedShadowComponentParentApp {} diff --git a/packages/platform-browser/test/dom/shadow_dom_spec.ts b/packages/platform-browser/test/dom/shadow_dom_spec.ts index 6e3e4fa01451..e04ebeae06c6 100644 --- a/packages/platform-browser/test/dom/shadow_dom_spec.ts +++ b/packages/platform-browser/test/dom/shadow_dom_spec.ts @@ -95,7 +95,16 @@ class ShadowComponent {} selector: 'styled-shadow-comp', template: '
    ', encapsulation: ViewEncapsulation.ShadowDom, - styles: [`:host { background: black; } .red { background: red; }`], + styles: [ + ` + :host { + background: black; + } + .red { + background: red; + } + `, + ], standalone: false, }) class StyledShadowComponent {} diff --git a/packages/platform-browser/test/testing_public_spec.ts b/packages/platform-browser/test/testing_public_spec.ts index f6855be8dcea..1e9bb4d51f92 100644 --- a/packages/platform-browser/test/testing_public_spec.ts +++ b/packages/platform-browser/test/testing_public_spec.ts @@ -41,7 +41,7 @@ import {isBrowser} from '@angular/private/testing'; @Component({ selector: 'child-comp', - template: `Original {{childBinding}}`, + template: `Original {{ childBinding }}`, standalone: false, }) @Injectable() @@ -103,7 +103,7 @@ class MockFancyService extends FancyService { @Component({ selector: 'my-service-comp', providers: [FancyService], - template: `injected value: {{fancyService.value}}`, + template: `injected value: {{ fancyService.value }}`, standalone: false, }) class TestProvidersComp { @@ -113,7 +113,7 @@ class TestProvidersComp { @Component({ selector: 'my-service-comp', viewProviders: [FancyService], - template: `injected value: {{fancyService.value}}`, + template: `injected value: {{ fancyService.value }}`, standalone: false, }) class TestViewProvidersComp { @@ -141,7 +141,7 @@ class SomePipe { @Component({ selector: 'comp', - template: `
    `, + template: `
    `, standalone: false, }) class CompUsingModuleDirectiveAndPipe {} diff --git a/packages/platform-server/init/test/shims_spec.ts b/packages/platform-server/init/test/shims_spec.ts index 80c57b249522..d19ab03eb55f 100644 --- a/packages/platform-server/init/test/shims_spec.ts +++ b/packages/platform-server/init/test/shims_spec.ts @@ -15,8 +15,8 @@ describe('applyShims()', () => { // Un-patch `global`. const currentProps = Object.keys(global); for (const prop of currentProps) { - if (prop === 'crypto') { - // crypto is a getter and cannot be changed + if (['navigator', 'crypto'].includes(prop)) { + // These props are just a getter and cannot be changed continue; } if (globalClone.hasOwnProperty(prop)) { diff --git a/packages/platform-server/test/event_replay_spec.ts b/packages/platform-server/test/event_replay_spec.ts index 275ad6f18b67..f40e42df61d5 100644 --- a/packages/platform-server/test/event_replay_spec.ts +++ b/packages/platform-server/test/event_replay_spec.ts @@ -93,9 +93,7 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - `, + template: ` `, }) class AppComponent { onClick = onClickSpy; @@ -119,9 +117,7 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - `, + template: ` `, }) class AppComponent_1 { onClick = onClickSpy; @@ -129,9 +125,7 @@ describe('event replay', () => { @Component({ selector: 'app-2', - template: ` - - `, + template: ` `, }) class AppComponent_2 { onClick() {} @@ -180,9 +174,7 @@ describe('event replay', () => { it('should cleanup `window._ejsas[appId]` once app is destroyed', async () => { @Component({ selector: 'app', - template: ` - - `, + template: ` `, }) class AppComponent { onClick() {} @@ -279,10 +271,9 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - - `, + template: ` + + `, imports: [AddGlobalListener], }) class AppComponent {} @@ -319,10 +310,9 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - - `, + template: ` + + `, imports: [AddGlobalListener], }) class AppComponent {} @@ -359,10 +349,9 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - - `, + template: ` + + `, imports: [AddGlobalListener], }) class AppComponent {} @@ -392,10 +381,10 @@ describe('event replay', () => { @Component({ selector: 'app', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class SimpleComponent { onClick() {} @@ -419,10 +408,10 @@ describe('event replay', () => { @Component({ selector: 'app', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class SimpleComponent { onClick() {} @@ -445,9 +434,7 @@ describe('event replay', () => { @Component({ selector: 'app', - template: ` - - `, + template: ` `, }) class AppComponent { constructor() { @@ -504,10 +491,10 @@ describe('event replay', () => { @Component({ selector: 'app', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class SimpleComponent { onClick = onClickSpy; @@ -535,10 +522,10 @@ describe('event replay', () => { @Component({ selector: 'app', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class SimpleComponent { onClick(e: Event) { @@ -568,10 +555,10 @@ describe('event replay', () => { @Component({ selector: 'app', template: ` -
    -
    -
    - `, +
    +
    +
    + `, }) class SimpleComponent { onClick(event: Event) { diff --git a/packages/platform-server/test/full_app_hydration_spec.ts b/packages/platform-server/test/full_app_hydration_spec.ts index 7e2033faa41f..3979b43951c1 100644 --- a/packages/platform-server/test/full_app_hydration_spec.ts +++ b/packages/platform-server/test/full_app_hydration_spec.ts @@ -125,9 +125,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [NestedComponent], - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -165,9 +163,7 @@ describe('platform-server full application hydration integration', () => { it('should skip embedded views from an ApplicationRef during annotation', async () => { @Component({ selector: 'app', - template: ` - Hi! - `, + template: ` Hi! `, }) class SimpleComponent { @ViewChild('tmpl', {read: TemplateRef}) tmplRef!: TemplateRef; @@ -190,9 +186,7 @@ describe('platform-server full application hydration integration', () => { it('should wipe out existing host element content when server side rendering', async () => { @Component({ selector: 'app', - template: ` -
    Some content
    - `, + template: `
    Some content
    `, }) class SimpleComponent {} @@ -234,9 +228,7 @@ describe('platform-server full application hydration integration', () => { it('should support text-only contents', async () => { @Component({ selector: 'app', - template: ` - This is hydrated content. - `, + template: ` This is hydrated content. `, }) class SimpleComponent {} @@ -312,9 +304,7 @@ describe('platform-server full application hydration integration', () => { it('should support a single text interpolation', async () => { @Component({ selector: 'app', - template: ` - {{ text }} - `, + template: ` {{ text }} `, }) class SimpleComponent { text = 'text'; @@ -440,9 +430,7 @@ describe('platform-server full application hydration integration', () => { it('should handle extra child nodes within a root app component', async () => { @Component({ selector: 'app', - template: ` -
    Some content
    - `, + template: `
    Some content
    `, }) class SimpleComponent {} @@ -469,9 +457,7 @@ describe('platform-server full application hydration integration', () => { it('should support empty containers', async () => { @Component({ selector: 'app', - template: ` - This is an empty container: - `, + template: ` This is an empty container: `, }) class SimpleComponent {} @@ -613,12 +599,12 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf], template: ` - This is a non-empty container: - -

    Hello world!

    -
    -
    Post-container element
    - `, + This is a non-empty container: + +

    Hello world!

    +
    +
    Post-container element
    + `, }) class SimpleComponent {} @@ -670,9 +656,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [NgIf], - template: ` -

    Hello world!

    - `, + template: `

    Hello world!

    `, }) class SimpleComponent {} @@ -696,9 +680,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [NgIf], - template: ` -

    Hello world!

    - `, + template: `

    Hello world!

    `, }) class SimpleComponent {} const html = await ssr(SimpleComponent); @@ -717,9 +699,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', imports: [NgIf], - template: ` -

    Hello World!

    - `, + template: `

    Hello World!

    `, }) class NestedComponent {} @@ -727,10 +707,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, NestedComponent], template: ` - This is a component: - -
    Post-container element
    - `, + This is a component: + +
    Post-container element
    + `, }) class SimpleComponent {} @@ -755,14 +735,14 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf], template: ` - This is a non-empty container: - -

    - Hello world! -

    -
    -
    Post-container element
    - `, + This is a non-empty container: + +

    + Hello world! +

    +
    +
    Post-container element
    + `, }) class SimpleComponent {} @@ -789,11 +769,11 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, NgFor], template: ` - -

    Item #{{ item }}

    -
    -
    Post-container element
    - `, + +

    Item #{{ item }}

    +
    +
    Post-container element
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -860,9 +840,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', imports: [NgIf], - template: ` -

    Hello World!

    - `, + template: `

    Hello World!

    `, }) class NestedComponent {} @@ -870,9 +848,9 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, NgFor, NestedComponent], template: ` - -
    Post-container element
    - `, + +
    Post-container element
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -908,7 +886,9 @@ describe('platform-server full application hydration integration', () => { Number {{ number }} is in [0, 5) range. is in [5, 8) range. - is in [8, 10) range. + is in [8, 10) range.
    `, }) @@ -945,17 +925,14 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', imports: [NgIf], - template: ` -

    Hello World!

    - `, + template: `

    Hello World!

    `, }) class NestedComponent {} @Component({ selector: 'app', imports: [NgComponentOutlet], - template: ` - `, + template: ` `, }) class SimpleComponent { // This field is necessary to expose @@ -983,18 +960,14 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', imports: [NgIf], - template: ` -

    Hello World!

    - `, + template: `

    Hello World!

    `, }) class NestedComponent {} @Component({ selector: 'app', imports: [NgComponentOutlet], - template: ` -
    - `, + template: `
    `, }) class SimpleComponent { // This field is necessary to expose @@ -1022,17 +995,14 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', imports: [NgIf], - template: ` -

    Hello World!

    - `, + template: `

    Hello World!

    `, }) class NestedComponent {} @Component({ selector: 'other-nested-cmp', imports: [NgComponentOutlet], - template: ` - `, + template: ` `, }) class OtherNestedComponent { // This field is necessary to expose @@ -1043,8 +1013,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [NgComponentOutlet], - template: ` - `, + template: ` `, }) class SimpleComponent { // This field is necessary to expose @@ -1075,9 +1044,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgTemplateOutlet], template: ` - - This is a content of the template! - + This is a content of the template! `, }) @@ -1104,9 +1071,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgTemplateOutlet], template: ` - - This is a content of the template! - + This is a content of the template!
    Some extra content
    `, @@ -1134,9 +1099,7 @@ describe('platform-server full application hydration integration', () => { it('should work with ViewContainerRef.createComponent', async () => { @Component({ selector: 'dynamic', - template: ` - This is a content of a dynamic component. - `, + template: ` This is a content of a dynamic component. `, }) class DynamicComponent {} @@ -1214,17 +1177,13 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [CommonModule], selector: 'dynamic', - template: ` - This is a content of a dynamic component. - `, + template: ` This is a content of a dynamic component. `, }) class DynamicComponent {} @Component({ selector: 'app', - template: ` -
    Hi! This is the main content.
    - `, + template: `
    Hi! This is the main content.
    `, }) class SimpleComponent { vcr = inject(ViewContainerRef); @@ -1302,27 +1261,21 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [CommonModule], selector: 'nested-dynamic-a', - template: ` -

    NestedDynamicComponentA

    - `, + template: `

    NestedDynamicComponentA

    `, }) class NestedDynamicComponentA {} @Component({ imports: [CommonModule], selector: 'nested-dynamic-b', - template: ` -

    NestedDynamicComponentB

    - `, + template: `

    NestedDynamicComponentB

    `, }) class NestedDynamicComponentB {} @Component({ imports: [CommonModule], selector: 'dynamic', - template: ` - This is a content of a dynamic component. - `, + template: ` This is a content of a dynamic component. `, }) class DynamicComponent { vcr = inject(ViewContainerRef); @@ -1335,9 +1288,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', - template: ` -
    Hi! This is the main content.
    - `, + template: `
    Hi! This is the main content.
    `, }) class SimpleComponent { doc = inject(DOCUMENT); @@ -1474,11 +1425,11 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'dynamic', template: ` - -

    Content of an embedded view

    -
    -
    Hi! This is the dynamic component content.
    - `, + +

    Content of an embedded view

    +
    +
    Hi! This is the dynamic component content.
    + `, }) class DynamicComponent { @ViewChild('tmpl', {read: TemplateRef}) tmpl!: TemplateRef; @@ -1534,19 +1485,19 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - -

    Content of H1

    -
    - -

    Content of H2

    -
    - -

    Content of H3

    -
    -

    Pre-container content

    - -
    Post-container content
    - `, + +

    Content of H1

    +
    + +

    Content of H2

    +
    + +

    Content of H3

    +
    +

    Pre-container content

    + +
    Post-container content
    + `, }) class SimpleComponent { @ViewChild('target', {read: ViewContainerRef}) vcr!: ViewContainerRef; @@ -1637,9 +1588,7 @@ describe('platform-server full application hydration integration', () => {

    Tag in between

    Some content - - Nested template content. - + Nested template content. `, }) @@ -1667,9 +1616,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [CommonModule], selector: 'insertion-component', - template: ` - - `, + template: ` `, }) class InsertionComponent { @Input() template!: TemplateRef; @@ -1738,9 +1685,7 @@ describe('platform-server full application hydration integration', () => { it('should not append skip hydration flag if component uses i18n blocks', async () => { @Component({ selector: 'app', - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class SimpleComponent {} @@ -1755,10 +1700,10 @@ describe('platform-server full application hydration integration', () => { imports: [NgIf], selector: 'app', template: ` -
    -
    Hi!
    -
    - `, +
    +
    Hi!
    +
    + `, }) class SimpleComponent {} @@ -1771,9 +1716,7 @@ describe('platform-server full application hydration integration', () => { it('should not append skip hydration flag if component uses i18n blocks on s', async () => { @Component({ selector: 'app', - template: ` - Hi! - `, + template: ` Hi! `, }) class SimpleComponent {} @@ -1787,9 +1730,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [CommonModule], selector: 'app', - template: ` - Hi! - `, + template: ` Hi! `, }) class SimpleComponent {} @@ -1835,13 +1776,19 @@ describe('platform-server full application hydration integration', () => { it('should support projecting translated content', async () => { @Component({ selector: 'app-content', - template: ``, + template: ``, }) class ContentComponent {} @Component({ selector: 'app', - template: `
    one
    two
    `, + template: `
    +
    one
    + two
    +
    `, imports: [ContentComponent], }) class SimpleComponent {} @@ -1927,8 +1874,7 @@ describe('platform-server full application hydration integration', () => { template: ` Span - Middle Start - Middle End + Middle Start Middle End
    Div
    `, @@ -1960,7 +1906,8 @@ describe('platform-server full application hydration integration', () => { it('should support disjoint nodes', async () => { @Component({ selector: 'app-content', - template: `Start Middle End`, + template: `Start Middle + End`, }) class ContentComponent {} @@ -1970,7 +1917,7 @@ describe('platform-server full application hydration integration', () => { Inner Start Span - { count, plural, other { Hello World! }} + {count, plural, other {Hello World!}} Inner End `, @@ -1999,14 +1946,15 @@ describe('platform-server full application hydration integration', () => { const content = clientRootNode.querySelector('app-content'); expect(content.innerHTML).toBe( - 'Start Inner Start Hello World! Inner End Middle Span End', + 'Start Inner Start Hello World! Inner End Middle Span End', ); }); it('should support nested content projection', async () => { @Component({ selector: 'app-content-inner', - template: `Start Middle End`, + template: `Start Middle + End`, }) class InnerContentComponent {} @@ -2023,7 +1971,7 @@ describe('platform-server full application hydration integration', () => { Outer Start Span - { count, plural, other { Hello World! }} + {count, plural, other {Hello World!}} Outer End `, @@ -2050,7 +1998,7 @@ describe('platform-server full application hydration integration', () => { const content = clientRootNode.querySelector('app-content-outer'); expect(content.innerHTML).toBe( - 'Start Outer Start Span Hello World! Outer End Middle End', + 'Start Outer Start Span Hello World! Outer End Middle End', ); }); @@ -2180,7 +2128,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', - template: `
    one
    two
    `, + template: `
    +
    one
    + two +
    `, }) class SimpleComponent { @ViewChild('target', {read: ViewContainerRef}) vcr!: ViewContainerRef; @@ -2224,7 +2175,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', - template: `
    one
    two
    `, + template: `
    +
    one
    + two +
    `, }) class SimpleComponent {} @@ -2319,7 +2273,12 @@ describe('platform-server full application hydration integration', () => { it('should cleanup dehydrated ICU cases', async () => { @Component({ selector: 'app', - template: `
    {isServer, select, true { This is a SERVER-ONLY content } false { This is a CLIENT-ONLY content }}
    `, + template: `
    + {isServer, select, + true {This is a SERVER-ONLY content} + false {This is a CLIENT-ONLY content} + } +
    `, }) class SimpleComponent { isServer = isPlatformServer(inject(PLATFORM_ID)) + ''; @@ -2360,7 +2319,10 @@ describe('platform-server full application hydration integration', () => { it('should hydrate ICUs (simple)', async () => { @Component({ selector: 'app', - template: `
    {{firstCase}} {firstCase, plural, =1 {item} other {items}}, {{secondCase}} {secondCase, plural, =1 {item} other {items}}
    `, + template: `
    + {{ firstCase }} {firstCase, plural, =1 {item} other {items}}, {{ secondCase }} + {secondCase, plural, =1 {item} other {items}} +
    `, }) class SimpleComponent { firstCase = 0; @@ -2385,13 +2347,18 @@ describe('platform-server full application hydration integration', () => { verifyClientAndSSRContentsMatch(ssrContents, clientRootNode); const div = clientRootNode.querySelector('div'); - expect(div.textContent).toBe('0 items, 1 item'); + expect(div.textContent).toBe(' 0 items, 1 item '); }); it('should hydrate ICUs (nested)', async () => { @Component({ selector: 'simple-component', - template: `
    {firstCase, select, 1 {one-{secondCase, select, 1 {one} 2 {two}}} 2 {two-{secondCase, select, 1 {one} 2 {two}}}}
    `, + template: `
    + {firstCase, select, + 1 {one-{secondCase, select, 1 {one} 2 {two}}} + 2 {two-{secondCase, select, 1 {one} 2 {two}}} + } +
    `, }) class SimpleComponent { @Input() firstCase!: number; @@ -2402,11 +2369,11 @@ describe('platform-server full application hydration integration', () => { imports: [SimpleComponent], selector: 'app', template: ` - - - - - `, + + + + + `, }) class AppComponent {} @@ -2427,23 +2394,19 @@ describe('platform-server full application hydration integration', () => { verifyAllNodesClaimedForHydration(clientRootNode); verifyClientAndSSRContentsMatch(ssrContents, clientRootNode); - expect(clientRootNode.querySelector('#one').textContent).toBe('one-one'); - expect(clientRootNode.querySelector('#two').textContent).toBe('one-two'); - expect(clientRootNode.querySelector('#three').textContent).toBe('two-one'); - expect(clientRootNode.querySelector('#four').textContent).toBe('two-two'); + expect(clientRootNode.querySelector('#one').textContent).toBe(' one-one '); + expect(clientRootNode.querySelector('#two').textContent).toBe(' one-two '); + expect(clientRootNode.querySelector('#three').textContent).toBe(' two-one '); + expect(clientRootNode.querySelector('#four').textContent).toBe(' two-two '); }); it('should hydrate containers', async () => { @Component({ selector: 'app', template: ` - - Container #1 - - - Container #2 - - `, + Container #1 + Container #2 + `, }) class SimpleComponent {} @@ -2475,10 +2438,10 @@ describe('platform-server full application hydration integration', () => { imports: [NgFor], selector: 'app', template: ` -
      -
    1. {{ item }}
    2. -
    - `, +
      +
    1. {{ item }}
    2. +
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -2509,12 +2472,12 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` -
      - @for (item of items; track $index) { -
    1. {{ item }}
    2. - } -
    - `, +
      + @for (item of items; track $index) { +
    1. {{ item }}
    2. + } +
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -2558,9 +2521,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CmpA], template: ` - - Some strong content - + Some strong content `, }) class SimpleComponent {} @@ -2602,9 +2563,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CmpA], template: ` - - Some strong content - + Some strong content `, }) class SimpleComponent {} @@ -2640,9 +2599,7 @@ describe('platform-server full application hydration integration', () => { it('should append skip hydration flag if component uses i18n blocks', async () => { @Component({ selector: 'app', - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class SimpleComponent {} @@ -2665,9 +2622,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', host: {ngSkipHydration: 'true'}, - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class SimpleComponent {} @@ -2691,10 +2646,10 @@ describe('platform-server full application hydration integration', () => { imports: [NgIf], selector: 'app', template: ` -
    -
    Hi!
    -
    - `, +
    +
    Hi!
    +
    + `, }) class SimpleComponent {} @@ -2716,9 +2671,7 @@ describe('platform-server full application hydration integration', () => { it('should append skip hydration flag if component uses i18n blocks on s', async () => { @Component({ selector: 'app', - template: ` - Hi! - `, + template: ` Hi! `, }) class SimpleComponent {} @@ -2741,9 +2694,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [CommonModule], selector: 'app', - template: ` - Hi! - `, + template: ` Hi! `, }) class SimpleComponent {} @@ -2765,9 +2716,7 @@ describe('platform-server full application hydration integration', () => { it('should *not* throw when i18n attributes are used', async () => { @Component({ selector: 'app', - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class SimpleComponent {} @@ -2793,9 +2742,7 @@ describe('platform-server full application hydration integration', () => { async () => { @Component({ selector: 'nested', - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class NestedComponent {} @@ -2803,9 +2750,9 @@ describe('platform-server full application hydration integration', () => { imports: [NestedComponent], selector: 'app', template: ` - Nested component with i18n inside: - - `, + Nested component with i18n inside: + + `, }) class SimpleComponent {} @@ -2829,9 +2776,7 @@ describe('platform-server full application hydration integration', () => { it('should exclude components with i18n from hydration automatically', async () => { @Component({ selector: 'nested', - template: ` -
    Hi!
    - `, + template: `
    Hi!
    `, }) class NestedComponent {} @@ -2839,10 +2784,10 @@ describe('platform-server full application hydration integration', () => { imports: [NestedComponent], selector: 'app', template: ` - Nested component with i18n inside - (the content of this component would be excluded from hydration): - - `, + Nested component with i18n inside (the content of this component would be excluded + from hydration): + + `, }) class SimpleComponent {} @@ -2947,6 +2892,7 @@ describe('platform-server full application hydration integration', () => { }) class MyLazyCmp {} + // prettier-ignore @Component({ selector: 'app', imports: [MyLazyCmp], @@ -3046,6 +2992,7 @@ describe('platform-server full application hydration integration', () => { }) class MyPlaceholderCmp {} + // prettier-ignore @Component({ selector: 'app', imports: [MyLazyCmp, MyPlaceholderCmp], @@ -3138,10 +3085,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'projector-cmp', template: ` -
    - -
    - `, +
    + +
    + `, }) class ProjectorCmp {} @@ -3214,10 +3161,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'projector-cmp', template: ` -
    - -
    - `, +
    + +
    + `, }) class ProjectorCmp {} @@ -3319,11 +3266,11 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [RegularComponent, ShadowDomComponent], template: ` -
    Main content
    - - - - `, +
    Main content
    + + + + `, }) class SimpleComponent {} @@ -3356,7 +3303,14 @@ describe('platform-server full application hydration integration', () => { imports: [NestedComponent], template: `
    Header
    - +
    Footer
    `, }) @@ -3383,9 +3337,7 @@ describe('platform-server full application hydration integration', () => { async () => { @Component({ selector: 'app', - template: ` -
    Main content
    - `, + template: `
    Main content
    `, }) class SimpleComponent {} @@ -3422,9 +3374,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested', imports: [NgIf], - template: ` - Hello world - `, + template: ` Hello world `, }) class Nested {} @@ -3432,11 +3382,11 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, Nested], template: ` - - - - - `, + + + + + `, }) class SimpleComponent {} @@ -3463,18 +3413,14 @@ describe('platform-server full application hydration integration', () => { async () => { @Component({ selector: 'regular-cmp', - template: ` - - `, + template: ` `, }) class RegularCmp {} @Component({ selector: 'deeply-nested', host: {ngSkipHydration: 'true'}, - template: ` - - `, + template: ` `, }) class DeeplyNested {} @@ -3483,10 +3429,10 @@ describe('platform-server full application hydration integration', () => { host: {ngSkipHydration: 'true'}, imports: [RegularCmp], template: ` - - - - `, + + + + `, }) class DeeplyNestedWrapper {} @@ -3494,12 +3440,12 @@ describe('platform-server full application hydration integration', () => { selector: 'layout', imports: [DeeplyNested, DeeplyNestedWrapper], template: ` - - - - - - `, + + + + + + `, }) class Layout {} @@ -3543,18 +3489,14 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'regular-cmp', - template: ` - - `, + template: ` `, }) class RegularCmp {} @Component({ selector: 'deeply-nested', host: {ngSkipHydration: 'true'}, - template: ` - - `, + template: ` `, }) class DeeplyNested {} @@ -3563,10 +3505,10 @@ describe('platform-server full application hydration integration', () => { host: {ngSkipHydration: 'true'}, imports: [RegularCmp], template: ` - - - - `, + + + + `, }) class DeeplyNestedWrapper {} @@ -3574,12 +3516,12 @@ describe('platform-server full application hydration integration', () => { selector: 'layout', imports: [DeeplyNested, DeeplyNestedWrapper], template: ` - - - - - - `, + + + + + + `, }) class Layout {} @@ -3631,18 +3573,14 @@ describe('platform-server full application hydration integration', () => { async () => { @Component({ selector: 'regular-cmp', - template: ` - - `, + template: ` `, }) class RegularCmp {} @Component({ selector: 'deeply-nested', host: {ngSkipHydration: 'true'}, - template: ` - - `, + template: ` `, }) class DeeplyNested {} @@ -3706,9 +3644,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested', imports: [NgIf], - template: ` - Hello world - `, + template: ` Hello world `, }) class Nested {} @@ -3716,11 +3652,11 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, Nested], template: ` - - - - - `, + + + + + `, }) class SimpleComponent {} @@ -3758,7 +3694,13 @@ describe('platform-server full application hydration integration', () => { imports: [NestedComponent], template: `
    Header
    - +
    Footer
    `, }) @@ -3798,9 +3740,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [NestedCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -3824,9 +3764,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', template: ` -

    Hello World!

    -
    This is the content of a nested component
    - `, +

    Hello World!

    +
    This is the content of a nested component
    + `, }) class NestedComponent { @Input() title = ''; @@ -3836,13 +3776,13 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NestedComponent], template: ` -
    Header
    - -

    Dehydrated content header

    -

    This content is definitely dehydrated and could use some water.

    -
    -
    Footer
    - `, +
    Header
    + +

    Dehydrated content header

    +

    This content is definitely dehydrated and could use some water.

    +
    +
    Footer
    + `, }) class SimpleComponent {} @@ -3866,9 +3806,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', template: ` -

    Hello World!

    -
    This is the content of a nested component
    - `, +

    Hello World!

    +
    This is the content of a nested component
    + `, }) class NestedComponent {} @@ -3876,14 +3816,14 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NestedComponent], template: ` -
    Header
    - - -

    Dehydrated content header

    -
    -
    -
    Footer
    - `, +
    Header
    + + +

    Dehydrated content header

    +
    +
    +
    Footer
    + `, }) class SimpleComponent {} @@ -3914,9 +3854,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', template: ` -

    Hello World!

    -
    This is the content of a nested component
    - `, +

    Hello World!

    +
    This is the content of a nested component
    + `, }) class NestedComponent { el = inject(ElementRef); @@ -3932,10 +3872,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NestedComponent], template: ` -
    Header
    - -
    Footer
    - `, +
    Header
    + +
    Footer
    + `, }) class SimpleComponent {} @@ -3960,11 +3900,11 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'nested-cmp', template: ` -

    Hello World!

    -
    -

    This content will be removed

    -
    - `, +

    Hello World!

    +
    +

    This content will be removed

    +
    + `, }) class NestedComponent { el = inject(ElementRef); @@ -3981,10 +3921,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NestedComponent], template: ` -
    Header
    - -
    Footer
    - `, +
    Header
    + +
    Footer
    + `, }) class SimpleComponent {} @@ -4027,10 +3967,10 @@ describe('platform-server full application hydration integration', () => { selector: 'projector-cmp', imports: [NestedComponent], template: ` -
    - -
    - `, +
    + +
    + `, }) class ProjectorCmp { vcr = inject(ViewContainerRef); @@ -4039,10 +3979,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - - `, + template: ` `, }) class SimpleComponent {} @@ -4069,9 +4006,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` -
    Header
    -
    Footer
    - `, +
    Header
    +
    Footer
    + `, }) class SimpleComponent {} @@ -4108,9 +4045,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [Dir], - template: ` -
    - `, + template: `
    `, }) class SimpleComponent {} @@ -4145,9 +4080,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', template: ` This is hydrated content. - {{spanText}}. -

    {{pText}}

    -
    {{anotherText}}
    + {{ spanText }}. +

    {{ pText }}

    +
    {{ anotherText }}
    `, }) class SimpleComponent { @@ -4179,10 +4115,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` -
    - {{ text }} -
    - `, +
    + {{ text }} +
    + `, }) class SimpleComponent { text = ''; @@ -4255,8 +4191,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - This is hydrated content.{{emptyText}}{{moreText}}{{andMoreText}}. -

    {{secondEmptyText}}{{secondMoreText}}

    + This is hydrated content.{{ emptyText }}{{ moreText }}{{ andMoreText }}. +

    {{ secondEmptyText }}{{ secondMoreText }}

    `, }) class SimpleComponent { @@ -4415,13 +4352,13 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf], template: ` - - This is a SERVER-ONLY content (embedded view) -
    This is a CLIENT-ONLY content (embedded view)
    -
    - This is a SERVER-ONLY content (root component) - This is a CLIENT-ONLY content (root component) - `, + + This is a SERVER-ONLY content (embedded view) +
    This is a CLIENT-ONLY content (embedded view)
    +
    + This is a SERVER-ONLY content (root component) + This is a CLIENT-ONLY content (root component) + `, }) class SimpleComponent { // This flag is intentionally different between the client @@ -4770,11 +4707,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - Projected content is just a plain text. - - `, + template: ` Projected content is just a plain text. `, }) class SimpleComponent {} @@ -4841,9 +4774,7 @@ describe('platform-server full application hydration integration', () => { - - Using ng-containers with *ngIf - + Using ng-containers with *ngIf @@ -4855,7 +4786,6 @@ describe('platform-server full application hydration integration', () => { - `, }) @@ -4950,11 +4880,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - Projected content is a plain text. - - `, + template: ` Projected content is a plain text. `, }) class SimpleComponent {} @@ -5083,9 +5009,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -5124,9 +5048,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -5156,16 +5078,14 @@ describe('platform-server full application hydration integration', () => { template: ` - `, + `, }) class ProjectorCmp {} @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -5191,10 +5111,10 @@ describe('platform-server full application hydration integration', () => { selector: 'projector-cmp', template: `
    - Header slot: - Main slot: - Footer slot: - + Header slot: Main slot: + Footer slot: + +
    `, }) @@ -5250,9 +5170,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -5293,9 +5211,7 @@ describe('platform-server full application hydration integration', () => { @Component({ imports: [ProjectorCmp], selector: 'app', - template: ` - - `, + template: ` `, }) class SimpleComponent {} @@ -5322,9 +5238,8 @@ describe('platform-server full application hydration integration', () => { selector: 'projector-cmp', template: `
    - Header slot: - Main slot: - Footer slot: + Header slot: Main slot: + Footer slot:
    `, @@ -5397,11 +5312,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [MenuComponent], - template: ` - - Menu Content - - `, + template: ` Menu Content `, }) class SimpleComponent {} @@ -5510,10 +5421,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'projector-cmp', template: ` -
    - -
    - `, +
    + +
    + `, }) class ProjectorCmp {} @@ -5527,13 +5438,13 @@ describe('platform-server full application hydration integration', () => { imports: [ProjectorCmp, NestedComponent], selector: 'app', template: ` - - -

    This node is not projected.

    -

    This node is not projected as well.

    -
    -
    - `, + + +

    This node is not projected.

    +

    This node is not projected as well.

    +
    +
    + `, }) class SimpleComponent {} @@ -5727,9 +5638,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app-shell', imports: [NgTemplateOutlet], - template: ` - - `, + template: ` `, }) class ShellCmp { @ContentChild('customTemplate', {static: true}) @@ -5740,11 +5649,11 @@ describe('platform-server full application hydration integration', () => { imports: [ShellCmp], selector: 'app', template: ` - - -

    template

    -
    -
    + + +

    template

    +
    +
    `, }) class SimpleComponent {} @@ -5812,9 +5721,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'dynamic', template: ` - - - `, + + + `, }) class DynamicComponent {} @@ -5822,9 +5731,9 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, NgFor], template: ` -
    -
    Hi! This is the main content.
    - `, +
    +
    Hi! This is the main content.
    + `, }) class SimpleComponent { @ViewChild('target', {read: ViewContainerRef}) vcr!: ViewContainerRef; @@ -5863,9 +5772,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'dynamic', template: ` - - - `, + + + `, }) class DynamicComponent {} @@ -5873,9 +5782,9 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgIf, NgFor], template: ` -
    -
    Hi! This is the main content.
    - `, +
    +
    Hi! This is the main content.
    + `, }) class SimpleComponent { @ViewChild('target', {read: ViewContainerRef}) vcr!: ViewContainerRef; @@ -5929,11 +5838,11 @@ describe('platform-server full application hydration integration', () => { imports: [ProjectorCmp], selector: 'app', template: ` - -

    This node is not projected.

    -

    This node is not projected as well.

    -
    - `, + +

    This node is not projected.

    +

    This node is not projected as well.

    +
    + `, }) class SimpleComponent { project = false; @@ -5992,11 +5901,11 @@ describe('platform-server full application hydration integration', () => { imports: [ProjectorCmp], selector: 'app', template: ` - -

    This node is projected.

    -

    This node is projected as well.

    -
    - `, + +

    This node is projected.

    +

    This node is projected as well.

    +
    + `, }) class SimpleComponent { project = true; @@ -6043,9 +5952,9 @@ describe('platform-server full application hydration integration', () => { selector: 'projector-cmp', template: `
    - Header slot: Header fallback - Main slot:
    Main fallback
    - Footer slot: Footer fallback {{expr}} + Header slot: Header fallback Main slot: +
    Main fallback
    Footer slot: + Footer fallback {{ expr }} Wildcard fallback
    `, @@ -6087,9 +5996,9 @@ describe('platform-server full application hydration integration', () => { selector: 'projector-cmp', template: `
    - Header slot: Header fallback - Main slot:
    Main fallback
    - Footer slot: Footer fallback {{expr}} + Header slot: Header fallback Main slot: +
    Main fallback
    Footer slot: + Footer fallback {{ expr }} Wildcard fallback
    `, @@ -6105,7 +6014,7 @@ describe('platform-server full application hydration integration', () => {
    Header override
    -

    Footer override {{expr}}

    +

    Footer override {{ expr }}

    `, @@ -6142,9 +6051,7 @@ describe('platform-server full application hydration integration', () => { it('should handle text node mismatch', async () => { @Component({ selector: 'app', - template: ` -
    This is an original content
    - `, + template: `
    This is an original content
    `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6179,9 +6086,9 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - Some text. -
    This is an original content
    - `, + Some text. +
    This is an original content
    + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6217,12 +6124,12 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` -
    -

    This is an original content

    - Bold text - Italic text -
    - `, +
    +

    This is an original content

    + Bold text + Italic text +
    + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6256,11 +6163,11 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - Bold text - -

    This is an original content

    -
    - `, + Bold text + +

    This is an original content

    +
    + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6298,12 +6205,12 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` -
    - -

    This is an original content

    -
    -
    - `, +
    + +

    This is an original content

    +
    +
    + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6340,9 +6247,9 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CommonModule], template: ` - Bold text - Italic text - `, + Bold text + Italic text + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6380,9 +6287,9 @@ describe('platform-server full application hydration integration', () => { selector: 'nested-cmp', imports: [CommonModule], template: ` - Bold text - Italic text - `, + Bold text + Italic text + `, }) class NestedComponent { private doc = inject(DOCUMENT); @@ -6428,12 +6335,12 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CommonModule], template: ` - - Bold text - Italic text - -
    Main content
    - `, + + Bold text + Italic text + +
    Main content
    + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6473,9 +6380,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [CommonModule, SimpleDir], - template: ` - Bold text - `, + template: ` Bold text `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6565,11 +6470,11 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CommonModule, ProjectorComponent], template: ` - - Bold text - Italic text - - `, + + Bold text + Italic text + + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6602,10 +6507,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [CommonModule, ProjectorComponent], template: ` - - Bold text - - `, + + Bold text + + `, }) class SimpleComponent { private doc = inject(DOCUMENT); @@ -6810,9 +6715,14 @@ describe('platform-server full application hydration integration', () => { This is NgIf CLIENT-ONLY content - @if (isServer) { This is new if SERVER-ONLY content } - @else { This is new if CLIENT-ONLY content } - @if (alwaysTrue) {

    CLIENT and SERVER content

    } + @if (isServer) { + This is new if SERVER-ONLY content + } @else { + This is new if CLIENT-ONLY content + } + @if (alwaysTrue) { +

    CLIENT and SERVER content

    + } `, }) class SimpleComponent { @@ -6886,9 +6796,9 @@ describe('platform-server full application hydration integration', () => { @if (true) { @if (true) {

    - @if (true) { - Hello world! - } + @if (true) { + Hello world! + }

    } } @@ -6967,16 +6877,20 @@ describe('platform-server full application hydration integration', () => { selector: 'app', imports: [NgSwitch, NgSwitchCase], template: ` - - This is NgSwitch SERVER-ONLY content - This is NgSwitch CLIENT-ONLY content - + + This is NgSwitch SERVER-ONLY content + This is NgSwitch CLIENT-ONLY content + - @switch (isServer) { - @case (true) { This is a SERVER-ONLY content } - @case (false) { This is a CLIENT-ONLY content } + @switch (isServer) { + @case (true) { + This is a SERVER-ONLY content } - `, + @case (false) { + This is a CLIENT-ONLY content + } + } + `, }) class SimpleComponent { // This flag is intentionally different between the client @@ -7041,11 +6955,15 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - @switch (label) { - @case ('A') { This is A } - @case ('B') { This is B } + @switch (label) { + @case ('A') { + This is A } - `, + @case ('B') { + This is B + } + } + `, }) class SimpleComponent { // This flag is intentionally different between the client @@ -7161,12 +7079,12 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - @for (item of items; track item) { -

    Item #{{ item }}

    - } @empty { -
    This is an "empty" block
    - } - `, + @for (item of items; track item) { +

    Item #{{ item }}

    + } @empty { +
    This is an "empty" block
    + } + `, }) class SimpleComponent { items = isPlatformServer(inject(PLATFORM_ID)) ? [] : [1, 2, 3]; @@ -7275,10 +7193,10 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', template: ` - @for (item of items; track item) { -

    Item #{{ item }}

    - } - `, + @for (item of items; track item) { +

    Item #{{ item }}

    + } + `, }) class SimpleComponent { // Item '3' is the same, the rest of the items are different. @@ -7327,10 +7245,10 @@ describe('platform-server full application hydration integration', () => { selector: 'app', template: ` - @for(item of items; track item) { -
    {{ item }}
    - } - `, + @for (item of items; track item) { +
    {{ item }}
    + } + `, }) class SimpleComponent { items = ['a', 'b', 'c']; @@ -7377,7 +7295,7 @@ describe('platform-server full application hydration integration', () => { selector: 'app', template: ` @let greeting = name + '!!!'; - Hello, {{greeting}} + Hello, {{ greeting }} `, }) class SimpleComponent { @@ -7413,7 +7331,7 @@ describe('platform-server full application hydration integration', () => { @let plusOne = value + 1; @let plusTwo = plusOne + 1; @let result = plusTwo + 1; - Result: {{result}} + Result: {{ result }} `, }) class SimpleComponent { @@ -7460,7 +7378,7 @@ describe('platform-server full application hydration integration', () => { imports: [DoublePipe], template: ` @let result = value | double; - Result: {{result}} + Result: {{ result }} `, }) class SimpleComponent { @@ -7496,7 +7414,7 @@ describe('platform-server full application hydration integration', () => { @if (true) { @if (true) { @let three = two + 1; - The result is {{three}} + The result is {{ three }} } @let two = one + 1; } @@ -7546,9 +7464,9 @@ describe('platform-server full application hydration integration', () => { template: ` @let one = 1; -
    |Footer value {{one}}
    +
    |Footer value {{ one }}
    @let two = one + 1; -
    Header value {{two}}|
    +
    Header value {{ two }}|
    `, imports: [InnerComponent], @@ -7584,7 +7502,7 @@ describe('platform-server full application hydration integration', () => { @let before = 'before'; @if (true) { @let inside = 'inside'; - {{before}}|{{inside}} + {{ before }}|{{ inside }} } `, }) @@ -7615,10 +7533,10 @@ describe('platform-server full application hydration integration', () => { @let before = 'before'; @if (true) { @let inside = 'inside'; - {{inside}} + {{ inside }} } @let after = 'after'; - {{before}}|{{after}} + {{ before }}|{{ after }} `, }) class SimpleComponent {} @@ -7647,7 +7565,7 @@ describe('platform-server full application hydration integration', () => { template: ` @let foo = ['foo']; @if (true) { - {{foo}} + {{ foo }} } `, }) @@ -7685,7 +7603,7 @@ describe('platform-server full application hydration integration', () => { @let a = 1; @let b = a + 1; - {{b}} + {{ b }} `, }) @@ -7901,9 +7819,7 @@ describe('platform-server full application hydration integration', () => { @Component({ selector: 'app', imports: [RouterOutlet], - template: ` - - `, + template: ` `, }) class SimpleComponent {} diff --git a/packages/platform-server/test/incremental_hydration_spec.ts b/packages/platform-server/test/incremental_hydration_spec.ts index 8ff166fc286c..0f5392f29c60 100644 --- a/packages/platform-server/test/incremental_hydration_spec.ts +++ b/packages/platform-server/test/incremental_hydration_spec.ts @@ -127,9 +127,9 @@ describe('platform-server partial hydration integration', () => { selector: 'dep-b', imports: [DepA], template: ` - - - `, + + + `, }) class DepB {} @@ -137,26 +137,26 @@ describe('platform-server partial hydration integration', () => { selector: 'app', imports: [DepB], template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } -
    +
    @defer (on viewport; hydrate on interaction) { -

    Nested defer block

    - +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } +
    + @defer (on viewport; hydrate on interaction) { +

    Nested defer block

    + + } @placeholder { + Inner block placeholder + } +
    } @placeholder { - Inner block placeholder + Outer block placeholder } -
    - } @placeholder { - Outer block placeholder - } -
    - `, + + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -193,9 +193,9 @@ describe('platform-server partial hydration integration', () => { selector: 'dep-b', imports: [DepA], template: ` - - - `, + + + `, }) class DepB {} @@ -203,26 +203,26 @@ describe('platform-server partial hydration integration', () => { selector: 'app', imports: [DepB], template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } -
    +
    @defer (on viewport; hydrate on interaction) { -

    Nested defer block

    - +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } +
    + @defer (on viewport; hydrate on interaction) { +

    Nested defer block

    + + } @placeholder { + Inner block placeholder + } +
    } @placeholder { - Inner block placeholder + Outer block placeholder } -
    - } @placeholder { - Outer block placeholder - } -
    - `, + + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -256,9 +256,9 @@ describe('platform-server partial hydration integration', () => { selector: 'dep-b', imports: [DepA], template: ` - - - `, + + + `, }) class DepB {} @@ -266,26 +266,26 @@ describe('platform-server partial hydration integration', () => { selector: 'app', imports: [DepB], template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } -
    - @defer (on viewport; hydrate on viewport) { -

    Nested defer block

    - +
    + @defer (on viewport; hydrate on interaction) { +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } +
    + @defer (on viewport; hydrate on viewport) { +

    Nested defer block

    + + } @placeholder { + Inner block placeholder + } +
    } @placeholder { - Inner block placeholder + Outer block placeholder } -
    - } @placeholder { - Outer block placeholder - } -
    - `, + + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -317,7 +317,7 @@ describe('platform-server partial hydration integration', () => { } @placeholder { Placeholder } - `, + `, }) class SimpleComponent {} @@ -343,25 +343,25 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } - - @defer (on viewport; hydrate on interaction) { -

    Nested defer block

    - } @placeholder { - Inner block placeholder - } -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (on viewport; hydrate on interaction) { +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } + + @defer (on viewport; hydrate on interaction) { +

    Nested defer block

    + } @placeholder { + Inner block placeholder + } +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -460,25 +460,25 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } -
    - @defer (on viewport; hydrate on interaction) { -

    Nested defer block

    - } @placeholder { - Inner block placeholder - } -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (on viewport; hydrate on interaction) { +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } +
    + @defer (on viewport; hydrate on interaction) { +

    Nested defer block

    + } @placeholder { + Inner block placeholder + } +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -570,25 +570,25 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on interaction) { -
    - Main defer block rendered! - @if (visible) { - Defer events work! - } -
    - @defer (on interaction) { -

    Nested defer block

    - } @placeholder { - Inner block placeholder - } -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on interaction) { +
    + Main defer block rendered! + @if (visible) { + Defer events work! + } +
    + @defer (on interaction) { +

    Nested defer block

    + } @placeholder { + Inner block placeholder + } +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { items = [1, 2, 3]; @@ -818,17 +818,15 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - defer block rendered! -
    - {{value()}} - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (on viewport; hydrate on interaction) { +
    defer block rendered!
    + {{ value() }} + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -885,17 +883,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - defer block rendered! - {{value()}} +
    + @defer (on viewport; hydrate on interaction) { +
    + defer block rendered! + {{ value() }}
    - } @placeholder { - Outer block placeholder - } -
    - `, + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -955,17 +953,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on hover) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on hover) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1027,17 +1025,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on hover) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on hover) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1197,17 +1195,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on viewport) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on viewport) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1282,14 +1280,12 @@ describe('platform-server partial hydration integration', () => { template: `
    @defer (hydrate on viewport({rootMargin: '123px', threshold: 0.5})) { -
    - defer block rendered! -
    +
    defer block rendered!
    } @placeholder { Outer block placeholder }
    - `, + `, }) class SimpleComponent {} @@ -1325,17 +1321,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on immediate) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on immediate) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1452,17 +1448,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on idle) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on idle) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1536,17 +1532,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate on timer(150)) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate on timer(150)) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1597,24 +1593,24 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on viewport; hydrate on interaction) { -
    - defer block rendered! - @defer (on viewport; hydrate on timer(150)) { -
    -

    Nested defer block

    - {{value()}} -
    - } @placeholder { - Inner block placeholder - } -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (on viewport; hydrate on interaction) { +
    + defer block rendered! + @defer (on viewport; hydrate on timer(150)) { +
    +

    Nested defer block

    + {{ value() }} +
    + } @placeholder { + Inner block placeholder + } +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1669,18 +1665,18 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on immediate; hydrate when iSaySo()) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } - -
    - `, +
    + @defer (on immediate; hydrate when iSaySo()) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } + +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1755,16 +1751,14 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (hydrate never) { -
    - defer block rendered! -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (hydrate never) { +
    defer block rendered!
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1818,17 +1812,17 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on timer(1s); hydrate never) { -
    - defer block rendered! - {{value()}} -
    - } @placeholder { - Outer block placeholder - } -
    - `, +
    + @defer (on timer(1s); hydrate never) { +
    + defer block rendered! + {{ value() }} +
    + } @placeholder { + Outer block placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -1893,30 +1887,30 @@ describe('platform-server partial hydration integration', () => { @Component({ selector: 'app', template: ` -
    - @defer (on timer(1s); hydrate never) { -
    - defer block rendered! - {{value()}} - @defer(on immediate; hydrate on idle) { -

    shouldn't be annotated

    - } @placeholder { -

    blah de blah

    - } -
    - } @placeholder { - Outer block placeholder - } - @defer (on timer(1s); hydrate on viewport) { -
    - viewport section -

    has a binding

    -
    - } @placeholder { - another placeholder - } -
    - `, +
    + @defer (on timer(1s); hydrate never) { +
    + defer block rendered! + {{ value() }} + @defer (on immediate; hydrate on idle) { +

    shouldn't be annotated

    + } @placeholder { +

    blah de blah

    + } +
    + } @placeholder { + Outer block placeholder + } + @defer (on timer(1s); hydrate on viewport) { +
    + viewport section +

    has a binding

    +
    + } @placeholder { + another placeholder + } +
    + `, }) class SimpleComponent { value = signal('start'); @@ -2045,9 +2039,9 @@ describe('platform-server partial hydration integration', () => {

    Main defer block rendered!

    @for (item of items; track $index) { @defer (on interaction; hydrate on interaction) { -
    - defer block {{item}} rendered! - {{value()}} +
    + defer block {{ item }} rendered! + {{ value() }}
    } @placeholder { Outer block placeholder @@ -2128,9 +2122,7 @@ describe('platform-server partial hydration integration', () => {

    Main defer block rendered!

    @if (isServer) { @defer (on interaction; hydrate on interaction) { -
    - nested defer block rendered! -
    +
    nested defer block rendered!
    } @placeholder { Outer block placeholder } @@ -2220,7 +2212,7 @@ describe('platform-server partial hydration integration', () => { } - `, + `, }) class SimpleComponent { @ViewChildren(NestedCmp) cmps!: QueryList; @@ -2777,9 +2769,9 @@ describe('platform-server partial hydration integration', () => { selector: 'nested-more', template: `
    - @defer(hydrate on immediate) { + @defer (hydrate on immediate) { -

    {{hydrated()}}

    +

    {{ hydrated() }}

    }
    `, @@ -2797,7 +2789,7 @@ describe('platform-server partial hydration integration', () => { imports: [NestedMoreCmp], template: `
    - @defer(hydrate on interaction) { + @defer (hydrate on interaction) { }
    diff --git a/packages/platform-server/test/integration_spec.ts b/packages/platform-server/test/integration_spec.ts index 2e973d1a2ee2..49f0a9a3384b 100644 --- a/packages/platform-server/test/integration_spec.ts +++ b/packages/platform-server/test/integration_spec.ts @@ -373,11 +373,10 @@ function createMyAnimationApp(standalone: boolean) { @Component({ standalone, selector: 'app', - template: ` -
    - - {{text}} -
    `, + template: `
    + + {{ text }} +
    `, animations: [ trigger('myAnimation', [ state('void', style({'opacity': '0'})), @@ -420,8 +419,7 @@ function createMyStylesApp(standalone: boolean) { @Component({ standalone, selector: 'app', - template: ` -
    Works!
    `, + template: `
    Works!
    `, styles: ['div {color: blue; } :host { color: red; }'], }) class MyStylesApp {} @@ -443,8 +441,7 @@ function createMyTransferStateApp(standalone: boolean) { @Component({ standalone, selector: 'app', - template: ` -
    Works!
    `, + template: `
    Works!
    `, }) class MyStylesApp { state = coreInject(TransferState); @@ -505,7 +502,7 @@ export class HttpInterceptorExampleModule {} @Component({ selector: 'app', - template: ``, + template: ``, standalone: false, }) class ImageApp {} @@ -1312,7 +1309,7 @@ class HiddenModule {} selector: 'app', template: ` Works! - + `, }) class MyServerApp {} diff --git a/packages/platform-server/test/transfer_state_spec.ts b/packages/platform-server/test/transfer_state_spec.ts index e8514212e55a..3fdc93666276 100644 --- a/packages/platform-server/test/transfer_state_spec.ts +++ b/packages/platform-server/test/transfer_state_spec.ts @@ -119,11 +119,11 @@ describe('transfer_state', () => { selector: 'app', imports: [Dep], template: ` - + @defer (hydrate on interaction) { } - `, + `, }) class SimpleComponent { constructor() { diff --git a/packages/router/src/components/empty_outlet.ts b/packages/router/src/components/empty_outlet.ts index 976c5d5c9e41..7911dbdab300 100644 --- a/packages/router/src/components/empty_outlet.ts +++ b/packages/router/src/components/empty_outlet.ts @@ -23,7 +23,7 @@ export {ɵEmptyOutletComponent as EmptyOutletComponent}; * to this `EmptyOutletComponent`. */ @Component({ - template: ``, + template: ``, imports: [RouterOutlet], // Used to avoid component ID collisions with user code. exportAs: 'emptyRouterOutlet', diff --git a/packages/router/src/directives/router_outlet.ts b/packages/router/src/directives/router_outlet.ts index be5c99e9fc90..3de1f9039f57 100644 --- a/packages/router/src/directives/router_outlet.ts +++ b/packages/router/src/directives/router_outlet.ts @@ -58,7 +58,7 @@ import {PRIMARY_OUTLET} from '../shared'; * @see [Page routerOutletData](guide/routing/show-routes-with-outlets#passing-contextual-data-to-routed-components) */ export const ROUTER_OUTLET_DATA = new InjectionToken>( - typeof ngDevMode !== undefined && ngDevMode ? 'RouterOutlet data' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'RouterOutlet data' : '', ); /** diff --git a/packages/router/src/events.ts b/packages/router/src/events.ts index be8382728a03..734432aefa1b 100644 --- a/packages/router/src/events.ts +++ b/packages/router/src/events.ts @@ -261,6 +261,14 @@ export class NavigationCancel extends RouterEvent { } } +export function isRedirectingEvent(event: Event): boolean { + return ( + event instanceof NavigationCancel && + (event.code === NavigationCancellationCode.Redirect || + event.code === NavigationCancellationCode.SupersededByNewNavigation) + ); +} + /** * An event triggered when a navigation is skipped. * This can happen for a couple reasons including onSameUrlHandling diff --git a/packages/router/src/navigation_transition.ts b/packages/router/src/navigation_transition.ts index b1e0f0756f67..077e67b85e5f 100644 --- a/packages/router/src/navigation_transition.ts +++ b/packages/router/src/navigation_transition.ts @@ -58,7 +58,7 @@ import { isRedirectingNavigationCancelingError, redirectingNavigationError, } from './navigation_canceling_error'; -import {activateRoutes} from './operators/activate_routes'; +import {ActivateRoutes} from './operators/activate_routes'; import {checkGuards} from './operators/check_guards'; import {recognize} from './operators/recognize'; import {resolveData} from './operators/resolve_data'; @@ -220,7 +220,7 @@ export type RestoredState = { /** * Information about a navigation operation. * Retrieve the most recent navigation object with the - * [Router.getCurrentNavigation() method](api/router/Router#getcurrentnavigation) . + * [Router.currentNavigation() method](api/router/Router#currentNavigation) . * * * *id* : The unique identifier of the current navigation. * * *initialUrl* : The target URL passed into the `Router#navigateByUrl()` call before navigation. @@ -725,6 +725,7 @@ export class NavigationTransitions { switchTap(() => this.afterPreactivation()), + // TODO(atscott): Move this into the last block below. switchMap(() => { const {currentSnapshot, targetSnapshot} = overallTransitionState; const viewTransitionStarted = this.createViewTransition?.( @@ -740,35 +741,56 @@ export class NavigationTransitions { : of(overallTransitionState); }), + // Ensure that if some observable used to drive the transition doesn't + // complete, the navigation still finalizes This should never happen, but + // this is done as a safety measure to avoid surfacing this error (#49567). + take(1), + map((t: NavigationTransition) => { const targetRouterState = createRouterState( router.routeReuseStrategy, t.targetSnapshot!, t.currentRouterState, ); - this.currentTransition = overallTransitionState = {...t, targetRouterState}; + this.currentTransition = overallTransitionState = t = {...t, targetRouterState}; this.currentNavigation.update((nav) => { nav!.targetRouterState = targetRouterState; return nav; }); - return overallTransitionState; - }), - tap(() => { this.events.next(new BeforeActivateRoutes()); - }), + if (!shouldContinueNavigation()) { + return; + } - activateRoutes( - this.rootContexts, - router.routeReuseStrategy, - (evt: Event) => this.events.next(evt), - this.inputBindingEnabled, - ), + new ActivateRoutes( + router.routeReuseStrategy, + overallTransitionState.targetRouterState!, + overallTransitionState.currentRouterState, + (evt: Event) => this.events.next(evt), + this.inputBindingEnabled, + ).activate(this.rootContexts); - // Ensure that if some observable used to drive the transition doesn't - // complete, the navigation still finalizes This should never happen, but - // this is done as a safety measure to avoid surfacing this error (#49567). - take(1), + if (!shouldContinueNavigation()) { + return; + } + + completedOrAborted = true; + this.currentNavigation.update((nav) => { + (nav as Writable).abort = noop; + return nav; + }); + this.lastSuccessfulNavigation.set(untracked(this.currentNavigation)); + this.events.next( + new NavigationEnd( + t.id, + this.urlSerializer.serialize(t.extractedUrl), + this.urlSerializer.serialize(t.urlAfterRedirects!), + ), + ); + this.titleStrategy?.updateTitle(t.targetRouterState!.snapshot); + t.resolve(true); + }), takeUntil( abortSignalToObservable(abortController.signal).pipe( @@ -785,23 +807,6 @@ export class NavigationTransitions { ), tap({ - next: (t: NavigationTransition) => { - completedOrAborted = true; - this.currentNavigation.update((nav) => { - (nav as Writable).abort = noop; - return nav; - }); - this.lastSuccessfulNavigation.set(untracked(this.currentNavigation)); - this.events.next( - new NavigationEnd( - t.id, - this.urlSerializer.serialize(t.extractedUrl), - this.urlSerializer.serialize(t.urlAfterRedirects!), - ), - ); - this.titleStrategy?.updateTitle(t.targetRouterState!.snapshot); - t.resolve(true); - }, complete: () => { completedOrAborted = true; }, @@ -849,6 +854,7 @@ export class NavigationTransitions { } }), catchError((e) => { + completedOrAborted = true; // If the application is already destroyed, the catch block should not // execute anything in practice because other resources have already // been released and destroyed. @@ -857,7 +863,6 @@ export class NavigationTransitions { return EMPTY; } - completedOrAborted = true; /* This error type is issued during Redirect, and is handled as a * cancellation rather than an error. */ if (isNavigationCancelingError(e)) { diff --git a/packages/router/src/operators/activate_routes.ts b/packages/router/src/operators/activate_routes.ts index ff46e0975ab3..4f4fa548d792 100644 --- a/packages/router/src/operators/activate_routes.ts +++ b/packages/router/src/operators/activate_routes.ts @@ -6,35 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ -import {MonoTypeOperatorFunction} from 'rxjs'; -import {map} from 'rxjs/operators'; - import {ActivationEnd, ChildActivationEnd, Event} from '../events'; -import type {NavigationTransition} from '../navigation_transition'; import type {DetachedRouteHandleInternal, RouteReuseStrategy} from '../route_reuse_strategy'; import type {ChildrenOutletContexts} from '../router_outlet_context'; import {ActivatedRoute, advanceActivatedRoute, RouterState} from '../router_state'; import {nodeChildrenAsMap, TreeNode} from '../utils/tree'; let warnedAboutUnsupportedInputBinding = false; - -export const activateRoutes = ( - rootContexts: ChildrenOutletContexts, - routeReuseStrategy: RouteReuseStrategy, - forwardEvent: (evt: Event) => void, - inputBindingEnabled: boolean, -): MonoTypeOperatorFunction => - map((t) => { - new ActivateRoutes( - routeReuseStrategy, - t.targetRouterState!, - t.currentRouterState, - forwardEvent, - inputBindingEnabled, - ).activate(rootContexts); - return t; - }); - export class ActivateRoutes { constructor( private routeReuseStrategy: RouteReuseStrategy, diff --git a/packages/router/src/router_config_loader.ts b/packages/router/src/router_config_loader.ts index e86d9a57c6d7..e2018309ab36 100644 --- a/packages/router/src/router_config_loader.ts +++ b/packages/router/src/router_config_loader.ts @@ -35,7 +35,7 @@ import {wrapIntoPromise} from './utils/collection'; * @publicApi */ export const ROUTES = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'ROUTES' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'ROUTES' : '', ); @Injectable({providedIn: 'root'}) diff --git a/packages/router/src/statemanager/navigation_state_manager.ts b/packages/router/src/statemanager/navigation_state_manager.ts index 66fa3eb5f993..fdd84222dfad 100644 --- a/packages/router/src/statemanager/navigation_state_manager.ts +++ b/packages/router/src/statemanager/navigation_state_manager.ts @@ -19,6 +19,7 @@ import {StateManager} from './state_manager'; import {RestoredState, Navigation as RouterNavigation} from '../navigation_transition'; import { BeforeActivateRoutes, + isRedirectingEvent, NavigationCancel, NavigationCancellationCode, NavigationEnd, @@ -78,6 +79,7 @@ export class NavigationStateManager extends StateManager { rejectNavigateEvent?: (reason?: any) => void; /** Function to resolve the intercepted navigation event. */ resolveHandler?: (v: void) => void; + navigationEvent?: NavigateEvent; } = {}; /** @@ -167,12 +169,29 @@ export class NavigationStateManager extends StateManager { removeAbortListener?.(); // Update `activeHistoryEntry` to the new current entry from Navigation API. this.activeHistoryEntry = this.navigation.currentEntry!; + // TODO(atscott): Consider initiating scroll here since it will be attempted periodically. + // We have to wait for render to resolve because focus reset is only done once in the spec. + // Render is not synchronous with NavigationEnd today. The Router's navigation promise resolve + // is what _causes_ the render to happen with ZoneJS... // Resolve handler after next render to defer scroll and focus reset. afterNextRender({read: () => resolveHandler?.()}, {injector: this.injector}); } } private createNavigationForTransition(transition: RouterNavigation) { + const {navigationEvent} = this.currentNavigation; + // If we are currently handling a traversal navigation, we do not need a new navigation for it + // because we are strictly restoring a previous state. If we are instead handling a navigation + // initiated outside the router, we do need to replace it with a router-triggered navigation + // to add the router-specific state. + if ( + navigationEvent && + (navigationEvent.navigationType === 'traverse' || + navigationEvent.navigationType === 'reload') && + this.eventAndRouterDestinationsMatch(navigationEvent, transition) + ) { + return; + } // Before we create a navigation for the Router transition, we have to remove any abort listeners // from the previous navigation event. Creating the new navigation will cause the signal // to be aborted, and we don't want that to cause our router transition to be aborted. @@ -202,6 +221,13 @@ export class NavigationStateManager extends StateManager { }; const info: NavigationInfo = {ɵrouterInfo: {intercept: true}}; + // https://issues.chromium.org/issues/460137775 - Bug in all browsers where URL might actually not be updated + // by the time we get here. replaceUrl was set to true in the Router when navigating to sync with the browser + // because it assumes the URL is already committed. In this scenario, we need to go back to 'push' behavior + // because it was not yet been committed and we should not replace the current entry. + if (!this.navigation.transition && this.currentNavigation.navigationEvent) { + transition.extras.replaceUrl = false; + } // Determine if this should be a 'push' or 'replace' history operation. const history = @@ -240,11 +266,7 @@ export class NavigationStateManager extends StateManager { const clearedState = {}; // Marker to detect if a new navigation started during async ops. this.currentNavigation = clearedState; // Do not reset state if we're redirecting or navigation is superseded by a new one. - if ( - cause instanceof NavigationCancel && - (cause.code === NavigationCancellationCode.SupersededByNewNavigation || - cause.code === NavigationCancellationCode.Redirect) - ) { + if (isRedirectingEvent(cause)) { return; } // Determine if the rollback should be a traversal to a specific previous entry @@ -266,11 +288,7 @@ export class NavigationStateManager extends StateManager { // indicating a new navigation has taken over. // We have no way of knowing if a navigation was aborted by another incoming navigation // https://github.com/WICG/navigation-api/issues/288 - if ( - cause instanceof NavigationCancel && - cause.code !== NavigationCancellationCode.GuardRejected && - cause.code !== NavigationCancellationCode.NoDataFromResolver - ) { + if (cause instanceof NavigationCancel && cause.code === NavigationCancellationCode.Aborted) { await Promise.resolve(); if (this.currentNavigation !== clearedState) { // A new navigation has started, so don't attempt to roll back this one. @@ -340,6 +358,7 @@ export class NavigationStateManager extends StateManager { } this.currentNavigation = {...this.currentNavigation}; + this.currentNavigation.navigationEvent = event; // Setup an abort handler. If the `NavigateEvent` is aborted (e.g., user clicks stop, // or another navigation supersedes this one), we need to abort the Angular Router's // internal navigation transition as well. @@ -404,6 +423,17 @@ export class NavigationStateManager extends StateManager { const state = event.destination.getState() as RestoredState | null | undefined; this.nonRouterCurrentEntryChangeSubject.next({path, state}); } + + private eventAndRouterDestinationsMatch( + navigateEvent: NavigateEvent, + transition: RouterNavigation, + ): boolean { + const internalPath = this.createBrowserPath(transition); + const eventDestination = new URL(navigateEvent.destination.url); + // this might be a path or an actual URL depending on the baseHref + const routerDestination = this.location.prepareExternalUrl(internalPath); + return new URL(routerDestination, eventDestination.origin).href === eventDestination.href; + } } /** diff --git a/packages/router/src/statemanager/state_manager.ts b/packages/router/src/statemanager/state_manager.ts index a6bd992af01d..dd32debbe840 100644 --- a/packages/router/src/statemanager/state_manager.ts +++ b/packages/router/src/statemanager/state_manager.ts @@ -13,8 +13,8 @@ import {SubscriptionLike} from 'rxjs'; import { BeforeActivateRoutes, Event, + isRedirectingEvent, NavigationCancel, - NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, @@ -212,11 +212,7 @@ export class HistoryStateManager extends StateManager { if (this.urlUpdateStrategy === 'deferred' && !currentTransition.extras.skipLocationChange) { this.setBrowserUrl(this.createBrowserPath(currentTransition), currentTransition); } - } else if ( - e instanceof NavigationCancel && - e.code !== NavigationCancellationCode.SupersededByNewNavigation && - e.code !== NavigationCancellationCode.Redirect - ) { + } else if (e instanceof NavigationCancel && !isRedirectingEvent(e)) { this.restoreHistory(currentTransition); } else if (e instanceof NavigationError) { this.restoreHistory(currentTransition, true); diff --git a/packages/router/src/utils/view_transition.ts b/packages/router/src/utils/view_transition.ts index 00886dcdd355..706f48b20ed3 100644 --- a/packages/router/src/utils/view_transition.ts +++ b/packages/router/src/utils/view_transition.ts @@ -12,11 +12,11 @@ import {afterNextRender, InjectionToken, Injector, runInInjectionContext} from ' import {ActivatedRouteSnapshot} from '../router_state'; export const CREATE_VIEW_TRANSITION = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'view transition helper' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'view transition helper' : '', ); export const VIEW_TRANSITION_OPTIONS = new InjectionToken< ViewTransitionsFeatureOptions & {skipNextTransition: boolean} ->(typeof ngDevMode !== undefined && ngDevMode ? 'view transition options' : ''); +>(typeof ngDevMode !== 'undefined' && ngDevMode ? 'view transition options' : ''); /** * Options to configure the View Transitions integration in the Router. @@ -99,6 +99,11 @@ export function createViewTransition( console.error(error); } }); + transition.finished.catch((error) => { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + console.error(error); + } + }); const {onViewTransitionCreated} = transitionOptions; if (onViewTransitionCreated) { runInInjectionContext(injector, () => onViewTransitionCreated({transition, from, to})); diff --git a/packages/router/test/bootstrap.spec.ts b/packages/router/test/bootstrap.spec.ts index 677563d9bbf9..98249b3b5471 100644 --- a/packages/router/test/bootstrap.spec.ts +++ b/packages/router/test/bootstrap.spec.ts @@ -485,14 +485,14 @@ describe('bootstrap', () => { @Component({ selector: 'component-a', template: ` -
    -
    -
    -
    -
    - -
    - `, +
    +
    +
    +
    +
    + +
    + `, standalone: false, }) class TallComponent {} diff --git a/packages/router/test/directives/router_outlet.spec.ts b/packages/router/test/directives/router_outlet.spec.ts index a5b197fb5703..f0b328430a55 100644 --- a/packages/router/test/directives/router_outlet.spec.ts +++ b/packages/router/test/directives/router_outlet.spec.ts @@ -100,10 +100,10 @@ describe('router outlet name', () => { it('should support outlets in ngFor', async () => { @Component({ template: ` -
    - -
    - `, +
    + +
    + `, imports: [RouterOutlet, NgForOf], }) class RootCmp { @@ -506,7 +506,7 @@ describe('router outlet data', () => { it('overrides parent provided data with nested', async () => { @Component({ imports: [RouterOutlet], - template: `{{outletData()}}|`, + template: `{{ outletData() }}|`, }) class Child { readonly outletData = inject(ROUTER_OUTLET_DATA); @@ -541,7 +541,7 @@ describe('router outlet data', () => { it('does not inherit ancestor data when not provided in nested', async () => { @Component({ imports: [RouterOutlet], - template: `{{outletData()}}|`, + template: `{{ outletData() }}|`, }) class Child { readonly outletData = inject(ROUTER_OUTLET_DATA); diff --git a/packages/router/test/integration/integration.spec.ts b/packages/router/test/integration/integration.spec.ts index 9b2a38327812..54a11c34edff 100644 --- a/packages/router/test/integration/integration.spec.ts +++ b/packages/router/test/integration/integration.spec.ts @@ -129,7 +129,7 @@ for (const browserAPI of ['navigation', 'history'] as const) { @Component({ selector: 'need-cd', - template: `{{'it works!'}}`, + template: `{{ 'it works!' }}`, standalone: false, }) class NeedCdCmp {} @@ -215,7 +215,9 @@ for (const browserAPI of ['navigation', 'history'] as const) { it('should work when an outlet is added/removed', async () => { @Component({ selector: 'someRoot', - template: `[
    ]`, + template: `[ +
    + ]`, standalone: false, }) class RootCmpWithLink { @@ -234,15 +236,15 @@ for (const browserAPI of ['navigation', 'history'] as const) { router.navigateByUrl('/simple'); await advance(fixture); - expect(fixture.nativeElement).toHaveText('[simple]'); + expect(fixture.nativeElement).toHaveText('[ simple ]'); fixture.componentInstance.cond.set(false); await advance(fixture); - expect(fixture.nativeElement).toHaveText('[]'); + expect(fixture.nativeElement).toHaveText('[ ]'); fixture.componentInstance.cond.set(true); await advance(fixture); - expect(fixture.nativeElement).toHaveText('[simple]'); + expect(fixture.nativeElement).toHaveText('[ simple ]'); }); it('should update location when navigating', async () => { @@ -863,7 +865,10 @@ for (const browserAPI of ['navigation', 'history'] as const) { it('should emit an event when an outlet gets activated', async () => { @Component({ selector: 'container', - template: ``, + template: ``, standalone: false, }) class Container { diff --git a/packages/router/test/integration/integration_helpers.ts b/packages/router/test/integration/integration_helpers.ts index a62b2b6cbc5d..e37202b40da0 100644 --- a/packages/router/test/integration/integration_helpers.ts +++ b/packages/router/test/integration/integration_helpers.ts @@ -76,9 +76,22 @@ export class AbsoluteLinkCmp {} @Component({ selector: 'link-cmp', - template: `link - - `, + template: `link + `, standalone: false, }) export class DummyLinkCmp { @@ -198,7 +211,7 @@ export class TwoOutletsCmp {} @Component({ selector: 'user-cmp', - template: `user {{name | async}}`, + template: `user {{ name | async }}`, standalone: false, }) export class UserCmp { @@ -224,7 +237,7 @@ export class WrapperCmp {} @Component({ selector: 'query-cmp', - template: `query: {{name | async}} fragment: {{fragment | async}}`, + template: `query: {{ name | async }} fragment: {{ fragment | async }}`, standalone: false, }) export class QueryParamsAndFragmentCmp { @@ -271,7 +284,8 @@ export class RouteCmp { @Component({ selector: 'link-cmp', - template: ` `, + template: ` + `, standalone: false, }) export class RelativeLinkInIfCmp { @@ -290,9 +304,9 @@ export class OutletInNgIf { @Component({ selector: 'link-cmp', template: ` - `, + `, standalone: false, }) export class DummyLinkWithParentCmp { @@ -339,7 +353,10 @@ export class RootCmpWithOnInit { @Component({ selector: 'root-cmp', - template: `primary [] right []`, + template: `primary [] right []`, standalone: false, }) export class RootCmpWithTwoOutlets {} diff --git a/packages/router/test/integration/lazy_loading.spec.ts b/packages/router/test/integration/lazy_loading.spec.ts index ff95a62d1a02..722add016258 100644 --- a/packages/router/test/integration/lazy_loading.spec.ts +++ b/packages/router/test/integration/lazy_loading.spec.ts @@ -1093,9 +1093,12 @@ export function lazyLoadingIntegrationSuite(browserAPI: 'navigation' | 'history' @Component({ selector: 'link-cmp', - template: `link - - `, + template: `link + `, standalone: false, }) class RelativeLinkCmp { diff --git a/packages/router/test/integration/navigation.spec.ts b/packages/router/test/integration/navigation.spec.ts index 0090c5a781ec..e0035dc761e9 100644 --- a/packages/router/test/integration/navigation.spec.ts +++ b/packages/router/test/integration/navigation.spec.ts @@ -393,7 +393,13 @@ export function navigationIntegrationTestSuite(browserAPI: 'history' | 'navigati // Angular does not support restoring state to the primitive. expect(navigation.extras.state).toEqual(undefined); - expect((location.getState() as any).navigationId).toBeDefined(); + // On a traversal, we really can't add state when using the navigation API. + // A traversal is a strict restoration of a previous state. To add our own state to the entry, + // we would need to perform a replaceState under the hood, and that would cancel/reject + // the traversal NavigateEvent and break scroll and focus restoration. + if (browserAPI === 'history') { + expect((location.getState() as any).navigationId).toBeDefined(); + } }); it('should not pollute browser history when replaceUrl is set to true', async () => { @@ -629,10 +635,10 @@ export function navigationIntegrationTestSuite(browserAPI: 'history' | 'navigati @Component({ template: ` - - - - `, + + + + `, standalone: false, }) class NamedOutletHost { diff --git a/packages/router/test/integration/route_reuse_strategy.spec.ts b/packages/router/test/integration/route_reuse_strategy.spec.ts index c7c4589a9678..b781341ba57b 100644 --- a/packages/router/test/integration/route_reuse_strategy.spec.ts +++ b/packages/router/test/integration/route_reuse_strategy.spec.ts @@ -102,7 +102,10 @@ export function routeReuseIntegrationSuite() { it('should emit an event when an outlet gets attached/detached', async () => { @Component({ selector: 'container', - template: ``, + template: ``, standalone: false, }) class Container { diff --git a/packages/router/test/integration/router_link_active.spec.ts b/packages/router/test/integration/router_link_active.spec.ts index 136556521350..78e25ac26152 100644 --- a/packages/router/test/integration/router_link_active.spec.ts +++ b/packages/router/test/integration/router_link_active.spec.ts @@ -161,10 +161,10 @@ export function routerLinkActiveIntegrationSuite() { it('should expose an isActive property', async () => { @Component({ template: ` -

    {{rla.isActive}}

    - - - `, +

    {{ rla.isActive }}

    + + + `, standalone: false, }) class ComponentWithRouterLink {} diff --git a/packages/router/test/integration/router_links.spec.ts b/packages/router/test/integration/router_links.spec.ts index d264b0fed4ff..9c389bbcc05b 100644 --- a/packages/router/test/integration/router_links.spec.ts +++ b/packages/router/test/integration/router_links.spec.ts @@ -117,11 +117,10 @@ export function routerLinkIntegrationSpec() { @Component({ selector: 'someCmp', template: ` - Link - - Link - - `, + Link + + Link + `, standalone: false, }) class CmpWithLink {} @@ -142,7 +141,8 @@ export function routerLinkIntegrationSpec() { it('should not throw when some command is null', async () => { @Component({ selector: 'someCmp', - template: `Link`, + template: `Link`, standalone: false, }) class CmpWithLink {} @@ -156,7 +156,8 @@ export function routerLinkIntegrationSpec() { it('should not throw when some command is undefined', async () => { @Component({ selector: 'someCmp', - template: `Link`, + template: `Link`, standalone: false, }) class CmpWithLink {} @@ -170,7 +171,8 @@ export function routerLinkIntegrationSpec() { it('should update hrefs when query params or fragment change', async () => { @Component({ selector: 'someRoot', - template: `Link`, + template: `Link`, standalone: false, }) class RootCmpWithLink {} @@ -198,7 +200,8 @@ export function routerLinkIntegrationSpec() { it('should correctly use the preserve strategy', async () => { @Component({ selector: 'someRoot', - template: `Link`, + template: `Link`, standalone: false, }) class RootCmpWithLink {} @@ -218,7 +221,13 @@ export function routerLinkIntegrationSpec() { it('should correctly use the merge strategy', async () => { @Component({ selector: 'someRoot', - template: `Link`, + template: `Link`, standalone: false, }) class RootCmpWithLink {} diff --git a/packages/router/test/page_title_strategy_spec.ts b/packages/router/test/page_title_strategy_spec.ts index c8ac31f3de20..cba3e357124e 100644 --- a/packages/router/test/page_title_strategy_spec.ts +++ b/packages/router/test/page_title_strategy_spec.ts @@ -192,9 +192,9 @@ export class BlankCmp {} @Component({ template: ` - - -`, + + + `, standalone: false, }) export class RootCmp {} diff --git a/packages/router/test/recognize.spec.ts b/packages/router/test/recognize.spec.ts index 9698a6a999ae..f25e054da583 100644 --- a/packages/router/test/recognize.spec.ts +++ b/packages/router/test/recognize.spec.ts @@ -693,6 +693,123 @@ describe('recognize', () => { checkActivatedRoute(s.root.firstChild!, 'bar', {}, ComponentA); }); }); + + describe('with wildcard and parameters', () => { + it('should support path parameter after a wildcard', async () => { + const s = await recognize( + [ + { + path: 'a', + component: ComponentA, + children: [ + { + path: '**/b/:id', + component: ComponentB, + }, + ], + }, + ], + 'a/1/2/b/3', + ); + const a = s.root.firstChild!; + checkActivatedRoute(a, 'a', {}, ComponentA); + + const wildcard = a.firstChild!; + checkActivatedRoute(wildcard, '1/2/b/3', {id: '3'}, ComponentB); + }); + + it('should support path parameter directly after a wildcard', async () => { + const s = await recognize( + [ + { + path: 'a', + component: ComponentA, + children: [ + { + path: '**/:id', + component: ComponentB, + }, + ], + }, + ], + 'a/1/2/3', + ); + const a = s.root.firstChild!; + checkActivatedRoute(a, 'a', {}, ComponentA); + + const wildcard = a.firstChild!; + checkActivatedRoute(wildcard, '1/2/3', {id: '3'}, ComponentB); + }); + + it('should support multiple path parameters after a wildcard', async () => { + const s = await recognize( + [ + { + path: 'a', + component: ComponentA, + children: [ + { + path: '**/:id1/:id2', + component: ComponentB, + }, + ], + }, + ], + 'a/1/2/3/4', + ); + const a = s.root.firstChild!; + checkActivatedRoute(a, 'a', {}, ComponentA); + + const wildcard = a.firstChild!; + checkActivatedRoute(wildcard, '1/2/3/4', {id1: '3', id2: '4'}, ComponentB); + }); + + it('should support path parameter before a wildcard', async () => { + const s = await recognize( + [ + { + path: 'a', + component: ComponentA, + children: [ + { + path: ':id/**', + component: ComponentB, + }, + ], + }, + ], + 'a/1/2/3', + ); + const a = s.root.firstChild!; + checkActivatedRoute(a, 'a', {}, ComponentA); + + const wildcard = a.firstChild!; + checkActivatedRoute(wildcard, '1/2/3', {id: '1'}, ComponentB); + }); + + it('should support multiple path parameters before a wildcard', async () => { + const s = await recognize( + [ + { + path: 'a', + component: ComponentA, + children: [ + { + path: ':id1/:id2/**', + component: ComponentB, + }, + ], + }, + ], + 'a/1/2/3/4', + ); + const a = s.root.firstChild!; + checkActivatedRoute(a, 'a', {}, ComponentA); + + const wildcard = a.firstChild!; + checkActivatedRoute(wildcard, '1/2/3/4', {id1: '1', id2: '2'}, ComponentB); + }); + }); }); describe('componentless routes', () => { diff --git a/packages/router/test/regression_integration.spec.ts b/packages/router/test/regression_integration.spec.ts index 2923beddf403..1d016d5963b3 100644 --- a/packages/router/test/regression_integration.spec.ts +++ b/packages/router/test/regression_integration.spec.ts @@ -55,11 +55,13 @@ describe('Integration', () => { it('should update when the associated routerLinks change - #18469', async () => { @Component({ template: ` - {{firstLink}} + {{ + firstLink + }} - `, + `, standalone: false, }) class LinkComponent { @@ -119,14 +121,13 @@ describe('Integration', () => { @Component({ selector: 'some-root', - template: ` -
    - -
    - - - - `, + template: `
    + +
    + + + + `, standalone: false, }) class MyCmp { @@ -157,7 +158,7 @@ describe('Integration', () => { @Component({ template: `
    - isActive: {{rla.isActive}} + isActive: {{ rla.isActive }} link @@ -213,10 +214,10 @@ describe('Integration', () => { it('should set isActive with OnPush change detection - #19934', async () => { @Component({ template: ` -
    - isActive: {{rla.isActive}} -
    - `, +
    + isActive: {{ rla.isActive }} +
    + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) @@ -411,8 +412,8 @@ describe('Integration', () => { it('should not unregister outlet if a different one already exists #36711, 32453', async () => { @Component({ template: ` - - + + `, standalone: false, }) @@ -532,7 +533,7 @@ describe('Integration', () => { @Component({ selector: 'test-app', - template: ` {{sideEffectWithRedirection()}} `, + template: ` {{ sideEffectWithRedirection() }} `, imports: [RouterOutlet], }) class App { diff --git a/packages/router/test/router_link_spec.ts b/packages/router/test/router_link_spec.ts index 54721f3e1d1d..a75589a2408e 100644 --- a/packages/router/test/router_link_spec.ts +++ b/packages/router/test/router_link_spec.ts @@ -45,7 +45,8 @@ describe('RouterLink', () => { [routerLink]="link()" [preserveFragment]="preserveFragment()" [skipLocationChange]="skipLocationChange()" - [replaceUrl]="replaceUrl()">
    + [replaceUrl]="replaceUrl()" + >
    `, standalone: false, }) @@ -126,7 +127,8 @@ describe('RouterLink', () => { [routerLink]="link()" [preserveFragment]="preserveFragment()" [skipLocationChange]="skipLocationChange()" - [replaceUrl]="replaceUrl()"> + [replaceUrl]="replaceUrl()" + > `, standalone: false, }) @@ -246,9 +248,7 @@ describe('RouterLink', () => { } @Component({ - template: ` - - `, + template: ` `, standalone: false, }) class LinkComponent { diff --git a/packages/router/test/standalone.spec.ts b/packages/router/test/standalone.spec.ts index 5c9b95c6b367..13ef8b00530c 100644 --- a/packages/router/test/standalone.spec.ts +++ b/packages/router/test/standalone.spec.ts @@ -113,7 +113,7 @@ describe('standalone in Router API', () => { } @Component({ - template: `{{service.value}}`, + template: `{{ service.value }}`, standalone: false, }) class MyComponent { @@ -145,7 +145,7 @@ describe('standalone in Router API', () => { class LazyModule {} @Component({ - template: `{{service.value}}`, + template: `{{ service.value }}`, standalone: false, }) class MyComponent { @@ -174,7 +174,7 @@ describe('standalone in Router API', () => { } @Component({ - template: `{{service.value}}`, + template: `{{ service.value }}`, standalone: false, }) class MyComponent { diff --git a/packages/router/test/view_transitions.spec.ts b/packages/router/test/view_transitions.spec.ts index 26c3cc0d1d0f..8993823f2832 100644 --- a/packages/router/test/view_transitions.spec.ts +++ b/packages/router/test/view_transitions.spec.ts @@ -85,7 +85,7 @@ describe('view transitions', () => { it('should not create a view transition if only the fragment changes', async () => { @Component({ selector: 'test-app', - template: `{{checks}}`, + template: `{{ checks }}`, }) class App { checks = 0; diff --git a/packages/router/test/with_platform_navigation.spec.ts b/packages/router/test/with_platform_navigation.spec.ts index bc680f6859fd..6ac226f60409 100644 --- a/packages/router/test/with_platform_navigation.spec.ts +++ b/packages/router/test/with_platform_navigation.spec.ts @@ -147,6 +147,18 @@ describe('withPlatformNavigation feature', () => { await timeout(); expect(navigation.transition).toBeNull(); }); + + it('retains the original traversal NavigateEvent', async () => { + router.resetConfig([{path: '**', children: []}]); + await router.navigateByUrl('/first'); + await timeout(); + + const navigateEvents: NavigateEvent[] = []; + navigation.addEventListener('navigate', (e: NavigateEvent) => navigateEvents.push(e)); + await navigation.back().finished; + expect(navigateEvents.length).toBe(1); + expect(navigateEvents[0].navigationType).toBe('traverse'); + }); }); describe('eager url update', () => { diff --git a/packages/service-worker/src/provider.ts b/packages/service-worker/src/provider.ts index 847f5da66db2..e9d0dd3f1a8a 100644 --- a/packages/service-worker/src/provider.ts +++ b/packages/service-worker/src/provider.ts @@ -26,7 +26,7 @@ import {SwUpdate} from './update'; import {RuntimeErrorCode} from './errors'; export const SCRIPT = new InjectionToken( - typeof ngDevMode !== undefined && ngDevMode ? 'NGSW_REGISTER_SCRIPT' : '', + typeof ngDevMode !== 'undefined' && ngDevMode ? 'NGSW_REGISTER_SCRIPT' : '', ); export function ngswAppInitializer(): void { diff --git a/packages/upgrade/BUILD.bazel b/packages/upgrade/BUILD.bazel index e7af2730746e..1f1322931fd6 100644 --- a/packages/upgrade/BUILD.bazel +++ b/packages/upgrade/BUILD.bazel @@ -60,6 +60,7 @@ generate_api_docs( srcs = [ ":files_for_docgen", "//packages:common_files_and_deps_for_docs", + "//packages/upgrade/src/common:files_for_docgen", ], entry_point = ":index.ts", module_name = "@angular/upgrade", diff --git a/packages/upgrade/public_api.ts b/packages/upgrade/public_api.ts index 68c71087b143..d4b925f1abd3 100644 --- a/packages/upgrade/public_api.ts +++ b/packages/upgrade/public_api.ts @@ -9,6 +9,8 @@ /** * @module * @description + * + * @deprecated Use the entry from `@angular/upgrade/static` instead. * Entry point for all public APIs of this package. allowing * Angular 1 and Angular 2+ to run side by side in the same application. */ diff --git a/packages/zone.js/MODULE.md b/packages/zone.js/MODULE.md index a60ab38953ae..b6013e612333 100644 --- a/packages/zone.js/MODULE.md +++ b/packages/zone.js/MODULE.md @@ -90,7 +90,7 @@ You can also disable specific `on` properties by setting `__Zone_ignore_on_prope Excluding `on` properties from being patched means that callbacks will always be invoked within the root context, regardless of where the `on` callback has been set. Even if `onclick` is set within a child zone, the callback will be called inside the root zone: ```ts -Zone.current.fork({ name: 'child' }).run(() => { +Zone.current.fork({name: 'child'}).run(() => { document.body.onclick = () => { console.log(Zone.current); // }; @@ -107,9 +107,9 @@ This package provides the following functionality: 1. **Error Inheritance:** Handle the `extend Error` issue: ```ts - class MyError extends Error {} - const myError = new MyError(); - console.log('is MyError instanceof Error', (myError instanceof Error)); + class MyError extends Error {} + const myError = new MyError(); + console.log('is MyError instanceof Error', myError instanceof Error); ``` Without the `zone-error` patch, the example above will output `false`. With the patch, the result will be `true`. diff --git a/packages/zone.js/NON-STANDARD-APIS.md b/packages/zone.js/NON-STANDARD-APIS.md index dde0a478fe2d..e74aae602477 100644 --- a/packages/zone.js/NON-STANDARD-APIS.md +++ b/packages/zone.js/NON-STANDARD-APIS.md @@ -247,7 +247,7 @@ Zone['__zone_symbol__jsonp']({ jsonp: getJSONP, sendFuncName: 'send', successFuncName: 'jsonpSuccessCallback', - failedFuncName: 'jsonpFailedCallback' + failedFuncName: 'jsonpFailedCallback', }); ``` diff --git a/packages/zone.js/STANDARD-APIS.md b/packages/zone.js/STANDARD-APIS.md index 3301fecff360..726bbfa49861 100644 --- a/packages/zone.js/STANDARD-APIS.md +++ b/packages/zone.js/STANDARD-APIS.md @@ -132,12 +132,7 @@ This can be done through `__Zone_ignore_on_properties` and `__zone_symbol__UNPAT - - - - -

    Basic Example

    - - - - - - - \ No newline at end of file + + + Zone.js Basic Demo + + + + + +

    Basic Example

    + + + + + + + diff --git a/packages/zone.js/example/benchmarks/addEventListener.html b/packages/zone.js/example/benchmarks/addEventListener.html index 050888a25564..c3805e1a2571 100644 --- a/packages/zone.js/example/benchmarks/addEventListener.html +++ b/packages/zone.js/example/benchmarks/addEventListener.html @@ -1,65 +1,63 @@ - + - - - Zone.js addEventListenerBenchmark - - - - - -

    addEventListener Benchmark

    - -

    No Zone

    - - - -

    With Zone

    - - - -
    - - + + +

    addEventListener Benchmark

    + +

    No Zone

    + + + +

    With Zone

    + + + +
    + + - + + diff --git a/packages/zone.js/example/counting.html b/packages/zone.js/example/counting.html index 839b8fd796ad..de72ec60b1ae 100644 --- a/packages/zone.js/example/counting.html +++ b/packages/zone.js/example/counting.html @@ -1,95 +1,94 @@ - + - - - Counting Pending Tasks - - - - - + + + Counting Pending Tasks + + + + + +

    Counting Pending Tasks

    -

    Counting Pending Tasks

    +

    + We want to know about just the events from a single mouse click while a bunch of other stuff + is happening on the page +

    -

    We want to know about just the events from a single mouse click - while a bunch of other stuff is happening on the page

    +

    + This is useful in E2E testing. Because you know when there are no async tasks, you avoid + adding timeouts that wait for tasks that run for an indeterminable amount of time. +

    -

    This is useful in E2E testing. Because you know when there are - no async tasks, you avoid adding timeouts that wait for tasks that - run for an indeterminable amount of time.

    + - +

    -

    - - - - + /* + * There may be other async actions going on in the background. + * Because this is not in the zone, our profiling ignores it. + * Nice. + */ + function noop() { + setTimeout(noop, 10 * Math.random()); + } + noop(); + + diff --git a/packages/zone.js/example/index.html b/packages/zone.js/example/index.html index 7fbea11508ea..73ff7daf72cc 100644 --- a/packages/zone.js/example/index.html +++ b/packages/zone.js/example/index.html @@ -1,21 +1,19 @@ - + - - - Zone.js Examples - - - + + + Zone.js Examples + + + +

    Examples

    -

    Examples

    - -
      -
    1. Tracing user actions with long stack traces
    2. -
    3. Counting Tasks
    4. -
    5. Profiling Across Tasks
    6. -
    7. Throttle
    8. -
    9. WebSocket
    10. -
    - - +
      +
    1. Tracing user actions with long stack traces
    2. +
    3. Counting Tasks
    4. +
    5. Profiling Across Tasks
    6. +
    7. Throttle
    8. +
    9. WebSocket
    10. +
    + diff --git a/packages/zone.js/example/profiling.html b/packages/zone.js/example/profiling.html index 4a097c9aeb51..964b586b9d38 100644 --- a/packages/zone.js/example/profiling.html +++ b/packages/zone.js/example/profiling.html @@ -1,126 +1,117 @@ - + - - - Zones Profiling - - - - - - + + + Zones Profiling + + + + + + +

    Profiling with Zones

    -

    Profiling with Zones

    + - + + /* + * This zone starts a timer at the start of each taskEnv, + * and stops it at the end. It accumulates the total run + * time internally, exposing it via `zone.time()` + * + * Note that this is the time the CPU is spending doing + * bogosort, as opposed to the time from the start + * of the algorithm until it's completion. + */ + var profilingZoneSpec = (function () { + var time = 0, + // use the high-res timer if available + timer = performance ? performance.now.bind(performance) : Date.now.bind(Date); + return { + onInvokeTask: function (delegate, current, target, task, applyThis, applyArgs) { + this.start = timer(); + delegate.invokeTask(target, task, applyThis, applyArgs); + time += timer() - this.start; + }, + time: function () { + return Math.floor(time * 100) / 100 + 'ms'; + }, + reset: function () { + time = 0; + }, + }; + })(); - + /* + * Bootstrap the app + */ + Zone.current.fork(profilingZoneSpec).run(main); + + diff --git a/packages/zone.js/example/throttle.html b/packages/zone.js/example/throttle.html index 69f29537ea02..e9d8ba6e1a9f 100644 --- a/packages/zone.js/example/throttle.html +++ b/packages/zone.js/example/throttle.html @@ -1,91 +1,86 @@ - + - - - Zones throttle - - - - - -

    Throttle Example

    + + + Zones throttle + + + + + +

    Throttle Example

    - + - + /* + * Start the application + * + * If we start the application with `zone.run`, we + * get long stack traces in the console! + */ - + //bootstrap(); + Zone.current.fork(Zone.longStackTraceZoneSpec).run(bootstrap); + + diff --git a/packages/zone.js/example/web-socket.html b/packages/zone.js/example/web-socket.html index 76a40cbb11f2..e2d10106e0fb 100644 --- a/packages/zone.js/example/web-socket.html +++ b/packages/zone.js/example/web-socket.html @@ -1,38 +1,42 @@ - + - - - WebSockets with Zones - - - - + + + WebSockets with Zones + + + + +

    + Ensure that you started node test/ws-server.js before loading this page. Then + check console output. +

    -

    - Ensure that you started node test/ws-server.js before loading - this page. Then check console output. -

    + - + + diff --git a/packages/zone.js/package.json b/packages/zone.js/package.json index e6df43d0b267..223368902596 100644 --- a/packages/zone.js/package.json +++ b/packages/zone.js/package.json @@ -22,9 +22,9 @@ "jest": "30.2.0", "mocha": "11.7.5", "mock-require": "3.0.3", - "jasmine": "5.12.0", + "jasmine": "5.13.0", "source-map-support": "0.5.21", - "jasmine-core": "5.12.1", + "jasmine-core": "5.13.0", "jasmine-reporters": "2.5.2", "rxjs": "7.8.2", "systemjs": "6.15.1", diff --git a/packages/zone.js/test/extra/bluebird.spec.ts b/packages/zone.js/test/extra/bluebird.spec.ts index 395966f7f4eb..306c54184b5a 100644 --- a/packages/zone.js/test/extra/bluebird.spec.ts +++ b/packages/zone.js/test/extra/bluebird.spec.ts @@ -110,15 +110,10 @@ describe('bluebird promise', () => { it('bluebird promise join method should be in zone', (done) => { zone.run(() => { - BluebirdPromise.join( + BluebirdPromise.join([ BluebirdPromise.resolve('test1'), BluebirdPromise.resolve('test2'), - (r1: string, r2: string) => { - expect(r1).toEqual('test1'); - expect(r2).toEqual('test2'); - expect(Zone.current.name).toEqual('bluebird'); - }, - ).then(() => { + ]).then(() => { expect(Zone.current.name).toEqual('bluebird'); expect(log.filter((item) => item === 'schedule bluebird task Promise.then').length).toBe(1); expect(log.filter((item) => item === 'invoke bluebird task Promise.then').length).toBe(1); diff --git a/packages/zone.js/test/performance/performance.html b/packages/zone.js/test/performance/performance.html index 37d5b59431f9..6908182e65dd 100644 --- a/packages/zone.js/test/performance/performance.html +++ b/packages/zone.js/test/performance/performance.html @@ -1,73 +1,65 @@ - + + + + + + + + + + + + - - - - - - - - - - - -
    Performance Bencnhmark of Zone.js vs Native Delegate!
    -
    -
    - - - - - - -
    - Module - - API - - Performance overhead -
    -
    -
    - + var tests = window['__zone_symbol__performance_tasks']; + window['__zone_symbol__testTargetsUIBuild']({ + tests: tests, + targetContainer: div, + resultsContainer: table, + jsonContainer: json, + jsonResult: jsonResult, + }); + }; + + + +
    Performance Bencnhmark of Zone.js vs Native Delegate!
    +
    +
    + + + + + + +
    ModuleAPIPerformance overhead
    +
    +
    + diff --git a/packages/zone.js/test/promise/promise.finally.spec.mjs b/packages/zone.js/test/promise/promise.finally.spec.mjs index cfe322214055..dac662a5659b 100644 --- a/packages/zone.js/test/promise/promise.finally.spec.mjs +++ b/packages/zone.js/test/promise/promise.finally.spec.mjs @@ -43,7 +43,7 @@ describe('onFinally', () => { }, function onRejected() { done(new Error('should not be called')); - } + }, ); }); @@ -62,7 +62,7 @@ describe('onFinally', () => { function onRejected(reason) { assert.strictEqual(reason, someRejectionReason); done(); - } + }, ); }); }); @@ -86,7 +86,7 @@ describe('onFinally', () => { function onRejected(reason) { assert.strictEqual(reason, someRejectionReason); done(); - } + }, ); }); @@ -104,7 +104,7 @@ describe('onFinally', () => { function onRejected(reason) { assert.strictEqual(reason, someRejectionReason); done(); - } + }, ); }); }); @@ -128,7 +128,7 @@ describe('onFinally', () => { }, function onRejected() { done(new Error('should not be called')); - } + }, ); }); @@ -150,7 +150,7 @@ describe('onFinally', () => { function onRejected(e) { assert.strictEqual(e, someRejectionReason); done(); - } + }, ); }); }); @@ -177,7 +177,7 @@ describe('onFinally', () => { function onRejected() { clearTimeout(timeout); done(new Error('should not be called')); - } + }, ); }); @@ -202,7 +202,7 @@ describe('onFinally', () => { function onRejected() { clearTimeout(timeout); done(new Error('should not be called')); - } + }, ); }); }); @@ -226,7 +226,7 @@ describe('onFinally', () => { }, function onRejected() { done(new Error('should not be called')); - } + }, ); }); @@ -248,7 +248,7 @@ describe('onFinally', () => { function onRejected(e) { assert.strictEqual(e, someRejectionReason); done(); - } + }, ); }); }); @@ -272,7 +272,7 @@ describe('onFinally', () => { function onRejected(e) { assert.strictEqual(e, 4); done(); - } + }, ); }); @@ -295,7 +295,7 @@ describe('onFinally', () => { function onRejected(e) { assert.strictEqual(e, newReason); done(); - } + }, ); }); }); @@ -325,7 +325,7 @@ describe('onFinally', () => { function onRejected() { clearTimeout(timeout); done(new Error('should not be called')); - } + }, ); }); @@ -353,7 +353,7 @@ describe('onFinally', () => { clearTimeout(timeout); assert.strictEqual(e, 3); done(); - } + }, ); }); }); @@ -383,7 +383,7 @@ describe('onFinally', () => { clearTimeout(timeout); assert.strictEqual(e, 4); done(); - } + }, ); }); @@ -411,7 +411,7 @@ describe('onFinally', () => { clearTimeout(timeout); assert.strictEqual(e, anotherReason); done(); - } + }, ); }); }); diff --git a/packages/zone.js/test/webdriver/test-es2015.html b/packages/zone.js/test/webdriver/test-es2015.html index 36cf44e694c3..0b54b31870d2 100644 --- a/packages/zone.js/test/webdriver/test-es2015.html +++ b/packages/zone.js/test/webdriver/test-es2015.html @@ -1,9 +1,9 @@ - + - - - - -
    Hello Zones!
    - + + + + +
    Hello Zones!
    + diff --git a/packages/zone.js/test/webdriver/test.html b/packages/zone.js/test/webdriver/test.html index b619bae1fc7b..84b5e29487a8 100644 --- a/packages/zone.js/test/webdriver/test.html +++ b/packages/zone.js/test/webdriver/test.html @@ -1,9 +1,9 @@ - + - - - - -
    Hello Zones!
    - + + + + +
    Hello Zones!
    + diff --git a/packages/zone.js/test/zone-spec/proxy.spec.ts b/packages/zone.js/test/zone-spec/proxy.spec.ts index 5236fd39b6a1..0d3e2be3c460 100644 --- a/packages/zone.js/test/zone-spec/proxy.spec.ts +++ b/packages/zone.js/test/zone-spec/proxy.spec.ts @@ -87,7 +87,7 @@ describe('ProxySpec', () => { zoneSpec: ZoneSpec, ) => { expect(currentZone).toBe(proxyZone); - expect(targetZone).toBe(proxyZone), expect(zoneSpec.name).toBe('fork2'); + (expect(targetZone).toBe(proxyZone), expect(zoneSpec.name).toBe('fork2')); called = true; }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c047ae32e79..a891c4b531f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,14 +22,14 @@ importers: .: dependencies: '@angular-devkit/build-angular': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(c5bc0fdbba09cbd593e90489895b6169) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(66265e176ffea27481c8d22d24371267) '@angular-devkit/core': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(chokidar@5.0.0) '@angular-devkit/schematics': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(chokidar@5.0.0) '@angular/animations': specifier: workspace:* version: link:packages/animations @@ -37,14 +37,14 @@ importers: specifier: 'workspace: *' version: link:packages/benchpress '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(703fb57f7d4265c06221da6c3dd7fb98) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(598663e591c40f71412182159bdbc21c) '@angular/cdk': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@types/node@20.19.25)(chokidar@5.0.0) '@angular/common': specifier: workspace:* version: link:packages/common @@ -70,8 +70,8 @@ importers: specifier: 'workspace: *' version: link:packages/localize '@angular/material': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/platform-browser': specifier: workspace:* version: link:packages/platform-browser @@ -88,8 +88,8 @@ importers: specifier: workspace:* version: link:packages/service-worker '@angular/ssr': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) '@angular/upgrade': specifier: 'workspace: *' version: link:packages/upgrade @@ -110,16 +110,16 @@ importers: version: 7.55.1(@types/node@20.19.25) '@rollup/plugin-babel': specifier: ^6.0.0 - version: 6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.53.3) + version: 6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.53.5) '@rollup/plugin-commonjs': specifier: ^29.0.0 - version: 29.0.0(rollup@4.53.3) + version: 29.0.0(rollup@4.53.5) '@rollup/plugin-node-resolve': specifier: ^16.0.0 - version: 16.0.3(rollup@4.53.3) + version: 16.0.3(rollup@4.53.5) '@schematics/angular': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(chokidar@5.0.0) '@standard-schema/spec': specifier: ^1.0.0 version: 1.0.0 @@ -199,8 +199,8 @@ importers: specifier: ^5.4.1 version: 5.6.2 chokidar: - specifier: ^4.0.0 - version: 4.0.3 + specifier: ^5.0.0 + version: 5.0.0 convert-source-map: specifier: ^1.5.1 version: 1.9.0 @@ -217,8 +217,8 @@ importers: specifier: https://github.com/angular/domino.git#93e720f143d0296dd2726ffbcf4fc12283363a7b version: '@angular/domino@https://codeload.github.com/angular/domino/tar.gz/93e720f143d0296dd2726ffbcf4fc12283363a7b' esbuild: - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 esbuild-plugin-umd-wrapper: specifier: ^3.0.0 version: 3.0.0 @@ -226,11 +226,11 @@ importers: specifier: ^14.0.0 version: 14.1.1 jasmine: - specifier: 5.12.0 - version: 5.12.0 + specifier: 5.13.0 + version: 5.13.0 jasmine-core: - specifier: 5.12.1 - version: 5.12.1 + specifier: 5.13.0 + version: 5.13.0 jasmine-reporters: specifier: ^2.5.2 version: 2.5.2 @@ -268,11 +268,11 @@ importers: specifier: ^0.2.0 version: 0.2.2 rollup: - specifier: 4.53.3 - version: 4.53.3 + specifier: 4.53.5 + version: 4.53.5 rollup-plugin-dts: specifier: ^6.1.1 - version: 6.2.3(rollup@4.53.3)(typescript@5.9.3) + version: 6.2.3(rollup@4.53.5)(typescript@5.9.3) rollup-plugin-preserve-shebang: specifier: ^1.0.1 version: 1.0.1 @@ -283,8 +283,8 @@ importers: specifier: 3.5.0 version: 3.5.0 selenium-webdriver4: - specifier: npm:selenium-webdriver@4.38.0 - version: selenium-webdriver@4.38.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: npm:selenium-webdriver@4.39.0 + version: selenium-webdriver@4.39.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) semver-dsl: specifier: ^1.0.1 version: 1.0.1 @@ -338,17 +338,17 @@ importers: version: 0.16.0 devDependencies: '@actions/core': - specifier: ^1.10.0 - version: 1.11.1 + specifier: ^2.0.0 + version: 2.0.0 '@actions/github': specifier: ^6.0.0 version: 6.0.1 '@angular-devkit/architect-cli': - specifier: 0.2101.0-next.0 - version: 0.2101.0-next.0(chokidar@4.0.3) + specifier: 0.2101.0-next.2 + version: 0.2101.0-next.2(chokidar@5.0.0) '@angular/ng-dev': - specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#4c28145df03aff8c74d0a53f4f5602140e4d1a23 - version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23(@modelcontextprotocol/sdk@1.22.0) + specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#24c98502339594196a800db33dd4294e359ea952 + version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/24c98502339594196a800db33dd4294e359ea952(@modelcontextprotocol/sdk@1.24.3) '@babel/plugin-proposal-async-generator-functions': specifier: 7.20.7 version: 7.20.7(@babel/core@7.28.5) @@ -362,8 +362,8 @@ importers: specifier: ^8.0.0 version: 8.2.1 '@bazel/ibazel': - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.28.0 + version: 0.28.0 '@inquirer/prompts': specifier: ^8.0.0 version: 8.0.1(@types/node@20.19.25) @@ -395,8 +395,8 @@ importers: specifier: ^0.5.10 version: 0.5.16 cldr: - specifier: 7.9.0 - version: 7.9.0 + specifier: 8.0.0 + version: 8.0.0 cldrjs: specifier: 0.5.5 version: 0.5.5 @@ -404,11 +404,11 @@ importers: specifier: ^7.0.0 version: 7.1.1(conventional-commits-filter@5.0.0) cypress: - specifier: 15.7.0 - version: 15.7.0 + specifier: 15.7.1 + version: 15.7.1 firebase-tools: - specifier: ^14.0.0 - version: 14.26.0(@types/node@20.19.25)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.5) + specifier: ^15.0.0 + version: 15.0.0(@types/node@20.19.25)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.5) get-tsconfig: specifier: ^4.10.1 version: 4.13.0 @@ -426,7 +426,7 @@ importers: version: 2.2.1 karma-jasmine-html-reporter: specifier: ^2.1.0 - version: 2.1.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 2.1.0(jasmine-core@5.13.0)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) karma-sauce-launcher: specifier: ^4.3.6 version: 4.3.6(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.5) @@ -435,7 +435,7 @@ importers: version: 3.6.2 rollup-plugin-sourcemaps2: specifier: ^0.5.1 - version: 0.5.4(@types/node@20.19.25)(rollup@4.53.3) + version: 0.5.4(@types/node@20.19.25)(rollup@4.53.5) semver: specifier: ^7.3.5 version: 7.7.3 @@ -470,32 +470,32 @@ importers: adev: dependencies: '@algolia/client-common': - specifier: 5.45.0 - version: 5.45.0 + specifier: 5.46.0 + version: 5.46.0 '@algolia/client-search': - specifier: 5.45.0 - version: 5.45.0 + specifier: 5.46.0 + version: 5.46.0 '@algolia/requester-browser-xhr': - specifier: 5.45.0 - version: 5.45.0 + specifier: 5.46.0 + version: 5.46.0 '@algolia/requester-node-http': - specifier: 5.45.0 - version: 5.45.0 + specifier: 5.46.0 + version: 5.46.0 '@angular/animations': specifier: workspace:* version: link:../packages/animations '@angular/aria': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/core@packages+core) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/core@packages+core) '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(2fee5caa4c7a4fbaa19a2f8f50e2bab6) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(94606abbceef2250d7cf197a30fc7802) '@angular/cdk': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@types/node@24.10.1)(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@types/node@24.10.4)(chokidar@5.0.0) '@angular/common': specifier: workspace:* version: link:../packages/common @@ -515,8 +515,8 @@ importers: specifier: workspace:* version: link:../packages/forms '@angular/material': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/platform-browser': specifier: workspace:* version: link:../packages/platform-browser @@ -527,8 +527,8 @@ importers: specifier: workspace:* version: link:../packages/router '@angular/ssr': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) '@codemirror/autocomplete': specifier: 6.20.0 version: 6.20.0 @@ -563,11 +563,11 @@ importers: specifier: 6.5.2 version: 6.5.2 '@codemirror/view': - specifier: 6.38.8 - version: 6.38.8 + specifier: 6.39.4 + version: 6.39.4 '@lezer/common': - specifier: 1.3.0 - version: 1.3.0 + specifier: 1.4.0 + version: 1.4.0 '@lezer/css': specifier: 1.3.0 version: 1.3.0 @@ -581,8 +581,8 @@ importers: specifier: 1.5.4 version: 1.5.4 '@lezer/lr': - specifier: 1.4.3 - version: 1.4.3 + specifier: 1.4.5 + version: 1.4.5 '@lezer/sass': specifier: 1.1.0 version: 1.1.0 @@ -602,8 +602,8 @@ importers: specifier: 27.0.0 version: 27.0.0 '@types/node': - specifier: 24.10.1 - version: 24.10.1 + specifier: 24.10.4 + version: 24.10.4 '@typescript/vfs': specifier: 1.6.2 version: 1.6.2(typescript@5.9.3) @@ -617,8 +617,8 @@ importers: specifier: 5.5.0 version: 5.5.0 algoliasearch: - specifier: 5.45.0 - version: 5.45.0 + specifier: 5.46.0 + version: 5.46.0 angular-split: specifier: 20.0.0 version: 20.0.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) @@ -635,11 +635,11 @@ importers: specifier: 0.8.2 version: 0.8.2 jasmine-core: - specifier: 5.12.1 - version: 5.12.1 + specifier: 5.13.0 + version: 5.13.0 jsdom: - specifier: 27.2.0 - version: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: 27.3.0 + version: 27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) karma-chrome-launcher: specifier: 3.2.0 version: 3.2.0 @@ -651,37 +651,37 @@ importers: version: 5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) karma-jasmine-html-reporter: specifier: 2.1.0 - version: 2.1.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 2.1.0(jasmine-core@5.13.0)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) marked: specifier: 17.0.1 version: 17.0.1 mermaid: - specifier: 11.12.1 - version: 11.12.1 + specifier: 11.12.2 + version: 11.12.2 ngx-progressbar: specifier: 14.0.0 - version: 14.0.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + version: 14.0.0(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) open-in-idx: specifier: 0.1.1 version: 0.1.1 playwright-core: - specifier: 1.56.1 - version: 1.56.1 + specifier: 1.57.0 + version: 1.57.0 preact: - specifier: 10.27.2 - version: 10.27.2 + specifier: 10.28.0 + version: 10.28.0 preact-render-to-string: - specifier: 6.6.3 - version: 6.6.3(preact@10.27.2) + specifier: 6.6.4 + version: 6.6.4(preact@10.28.0) prettier: - specifier: 3.6.2 - version: 3.6.2 + specifier: 3.7.4 + version: 3.7.4 rxjs: specifier: 7.8.2 version: 7.8.2 shiki: - specifier: 3.15.0 - version: 3.15.0 + specifier: 3.20.0 + version: 3.20.0 style-mod: specifier: 4.1.3 version: 4.1.3 @@ -699,8 +699,8 @@ importers: version: 2.2.8 devDependencies: autoprefixer: - specifier: 10.4.22 - version: 10.4.22(postcss@8.5.6) + specifier: 10.4.23 + version: 10.4.23(postcss@8.5.6) karma: specifier: ~6.4.4 version: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) @@ -708,8 +708,8 @@ importers: specifier: 8.5.6 version: 8.5.6 tailwindcss: - specifier: 3.4.18 - version: 3.4.18(tsx@4.20.6)(yaml@2.8.1) + specifier: 3.4.19 + version: 3.4.19(tsx@4.21.0)(yaml@2.8.2) adev/shared-docs: dependencies: @@ -741,8 +741,8 @@ importers: specifier: ^0.8.2 version: 0.8.2 jsdom: - specifier: ~27.2.0 - version: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: ~27.3.0 + version: 27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) marked: specifier: ~17.0.0 version: 17.0.0 @@ -792,8 +792,8 @@ importers: specifier: workspace:* version: link:../packages/router '@angular/ssr': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) rxjs: specifier: ~7.8.0 version: 7.8.2 @@ -802,11 +802,11 @@ importers: version: 2.8.1 devDependencies: '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(2fee5caa4c7a4fbaa19a2f8f50e2bab6) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(e6f8c05c913935fcb8ae5b7a4b4218f6) '@angular/cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@types/node@24.10.1)(chokidar@4.0.3) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(@types/node@24.10.4)(chokidar@5.0.0) '@angular/compiler-cli': specifier: workspace:* version: link:../packages/compiler-cli @@ -818,7 +818,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.0 - version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) integration: dependencies: @@ -874,8 +874,8 @@ importers: specifier: workspace:* version: link:../packages/benchpress '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(2fee5caa4c7a4fbaa19a2f8f50e2bab6) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(94606abbceef2250d7cf197a30fc7802) '@angular/common': specifier: workspace:* version: link:../packages/common @@ -913,8 +913,8 @@ importers: specifier: 2.0.13 version: 2.0.13 '@types/node': - specifier: 24.10.1 - version: 24.10.1 + specifier: 24.10.4 + version: 24.10.4 '@types/selenium-webdriver': specifier: 3.0.7 version: 3.0.7 @@ -959,7 +959,7 @@ importers: dependencies: '@angular/core': specifier: ^21.1.0-next - version: 21.1.0-next.0(@angular/compiler@21.1.0-next.0) + version: 21.1.0-next.0(@angular/compiler@packages+compiler) reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -994,8 +994,8 @@ importers: specifier: ^1.4.14 version: 1.5.5 chokidar: - specifier: ^4.0.0 - version: 4.0.3 + specifier: ^5.0.0 + version: 5.0.0 convert-source-map: specifier: ^1.5.1 version: 1.9.0 @@ -1045,8 +1045,8 @@ importers: specifier: workspace:* version: link:../../../animations '@angular/build': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(2fee5caa4c7a4fbaa19a2f8f50e2bab6) + specifier: 21.1.0-next.2 + version: 21.1.0-next.2(94606abbceef2250d7cf197a30fc7802) '@angular/common': specifier: workspace:* version: link:../../../common @@ -1092,6 +1092,9 @@ importers: '@angular/platform-browser': specifier: 'workspace: *' version: link:../platform-browser + '@standard-schema/spec': + specifier: ^1.0.0 + version: 1.0.0 rxjs: specifier: ^6.5.3 || ^7.4.0 version: 7.8.2 @@ -1261,11 +1264,11 @@ importers: specifier: 3.0.0 version: 3.0.0 jasmine: - specifier: 5.12.0 - version: 5.12.0 + specifier: 5.13.0 + version: 5.13.0 jasmine-core: - specifier: 5.12.1 - version: 5.12.1 + specifier: 5.13.0 + version: 5.13.0 jasmine-reporters: specifier: 2.5.2 version: 2.5.2 @@ -1298,7 +1301,7 @@ importers: version: 2.8.1 vitest: specifier: ^4.0.0 - version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) packages/zone.js/test/typings: dependencies: @@ -1347,11 +1350,11 @@ importers: specifier: ~3.7.0 version: 3.7.1 jasmine: - specifier: ~5.12.0 - version: 5.12.0 + specifier: ~5.13.0 + version: 5.13.0 jasmine-core: - specifier: ~5.12.0 - version: 5.12.1 + specifier: ~5.13.0 + version: 5.13.0 jasmine-reporters: specifier: ~2.5.2 version: 2.5.2 @@ -1398,24 +1401,24 @@ importers: vscode-ng-language-service/integration/project: dependencies: '@angular/common': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/core@21.1.0-next.0(@angular/compiler@21.1.0-next.0))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0 + specifier: 21.1.0-next.4 + version: 21.1.0-next.4 '@angular/compiler-cli': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler@21.1.0-next.0)(typescript@5.9.3) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) '@angular/core': - specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler@21.1.0-next.0) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2) rxjs: specifier: 7.8.2 version: 7.8.2 devDependencies: ng-packagr: specifier: 21.1.0-next.0 - version: 21.1.0-next.0(@angular/compiler-cli@21.1.0-next.0(@angular/compiler@21.1.0-next.0)(typescript@5.9.3))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3) + version: 21.1.0-next.0(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3) typescript: specifier: 5.9.3 version: 5.9.3 @@ -1433,8 +1436,8 @@ importers: specifier: ^24.5.2 version: 24.10.1 vscode-html-languageservice: - specifier: 5.6.0 - version: 5.6.0 + specifier: 5.6.1 + version: 5.6.1 vscode-languageserver: specifier: 7.0.0 version: 7.0.0 @@ -1450,75 +1453,90 @@ packages: '@acemir/cssom@0.9.24': resolution: {integrity: sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==} - '@actions/core@1.11.1': - resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} + '@acemir/cssom@0.9.28': + resolution: {integrity: sha512-LuS6IVEivI75vKN8S04qRD+YySP0RmU/cV8UNukhQZvprxF+76Z43TNo/a08eCodaGhT1Us8etqS1ZRY9/Or0A==} + + '@actions/core@2.0.0': + resolution: {integrity: sha512-iGW52/zqhPUFnYl0s1ioXfJu86LGs7b+GYuO38JMPpsh14FQrNj3n2JBpC+vZ2CFS4lERQyn5koLDopY+6V/PQ==} + + '@actions/core@2.0.1': + resolution: {integrity: sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg==} '@actions/exec@1.1.1': resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + '@actions/exec@2.0.0': + resolution: {integrity: sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==} + '@actions/github@6.0.1': resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==} '@actions/http-client@2.2.3': resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + '@actions/http-client@3.0.0': + resolution: {integrity: sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==} + '@actions/io@1.1.3': resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} - '@algolia/abtesting@1.11.0': - resolution: {integrity: sha512-a7oQ8dwiyoyVmzLY0FcuBqyqcNSq78qlcOtHmNBumRlHCSnXDcuoYGBGPN1F6n8JoGhviDDsIaF/oQrzTzs6Lg==} + '@actions/io@2.0.0': + resolution: {integrity: sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==} + + '@algolia/abtesting@1.12.0': + resolution: {integrity: sha512-EfW0bfxjPs+C7ANkJDw2TATntfBKsFiy7APh+KO0pQ8A6HYa5I0NjFuCGCXWfzzzLXNZta3QUl3n5Kmm6aJo9Q==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.45.0': - resolution: {integrity: sha512-WTW0VZA8xHMbzuQD5b3f41ovKZ0MNTIXkWfm0F2PU+XGcLxmxX15UqODzF2sWab0vSbi3URM1xLhJx+bXbd1eQ==} + '@algolia/client-abtesting@5.46.0': + resolution: {integrity: sha512-eG5xV8rujK4ZIHXrRshvv9O13NmU/k42Rnd3w43iKH5RaQ2zWuZO6Q7XjaoJjAFVCsJWqRbXzbYyPGrbF3wGNg==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.45.0': - resolution: {integrity: sha512-I3g7VtvG/QJOH3tQO7E7zWTwBfK/nIQXShFLR8RvPgWburZ626JNj332M3wHCYcaAMivN9WJG66S2JNXhm6+Xg==} + '@algolia/client-analytics@5.46.0': + resolution: {integrity: sha512-AYh2uL8IUW9eZrbbT+wZElyb7QkkeV3US2NEKY7doqMlyPWE8lErNfkVN1NvZdVcY4/SVic5GDbeDz2ft8YIiQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.45.0': - resolution: {integrity: sha512-/nTqm1tLiPtbUr+8kHKyFiCOfhRfgC+JxLvOCq471gFZZOlsh6VtFRiKI60/zGmHTojFC6B0mD80PB7KeK94og==} + '@algolia/client-common@5.46.0': + resolution: {integrity: sha512-0emZTaYOeI9WzJi0TcNd2k3SxiN6DZfdWc2x2gHt855Jl9jPUOzfVTL6gTvCCrOlT4McvpDGg5nGO+9doEjjig==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.45.0': - resolution: {integrity: sha512-suQTx/1bRL1g/K2hRtbK3ANmbzaZCi13487sxxmqok+alBDKKw0/TI73ZiHjjFXM2NV52inwwcmW4fUR45206Q==} + '@algolia/client-insights@5.46.0': + resolution: {integrity: sha512-wrBJ8fE+M0TDG1As4DDmwPn2TXajrvmvAN72Qwpuv8e2JOKNohF7+JxBoF70ZLlvP1A1EiH8DBu+JpfhBbNphQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.45.0': - resolution: {integrity: sha512-CId/dbjpzI3eoUhPU6rt/z4GrRsDesqFISEMOwrqWNSrf4FJhiUIzN42Ac+Gzg69uC0RnzRYy60K1y4Na5VSMw==} + '@algolia/client-personalization@5.46.0': + resolution: {integrity: sha512-LnkeX4p0ENt0DoftDJJDzQQJig/sFQmD1eQifl/iSjhUOGUIKC/7VTeXRcKtQB78naS8njUAwpzFvxy1CDDXDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.45.0': - resolution: {integrity: sha512-tjbBKfA8fjAiFtvl9g/MpIPiD6pf3fj7rirVfh1eMIUi8ybHP4ovDzIaE216vHuRXoePQVCkMd2CokKvYq1CLw==} + '@algolia/client-query-suggestions@5.46.0': + resolution: {integrity: sha512-aF9tc4ex/smypXw+W3lBPB1jjKoaGHpZezTqofvDOI/oK1dR2sdTpFpK2Ru+7IRzYgwtRqHF3znmTlyoNs9dpA==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.45.0': - resolution: {integrity: sha512-nxuCid+Nszs4xqwIMDw11pRJPes2c+Th1yup/+LtpjFH8QWXkr3SirNYSD3OXAeM060HgWWPLA8/Fxk+vwxQOA==} + '@algolia/client-search@5.46.0': + resolution: {integrity: sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.45.0': - resolution: {integrity: sha512-t+1doBzhkQTeOOjLHMlm4slmXBhvgtEGQhOmNpMPTnIgWOyZyESWdm+XD984qM4Ej1i9FRh8VttOGrdGnAjAng==} + '@algolia/ingestion@1.46.0': + resolution: {integrity: sha512-2LT0/Z+/sFwEpZLH6V17WSZ81JX2uPjgvv5eNlxgU7rPyup4NXXfuMbtCJ+6uc4RO/LQpEJd3Li59ke3wtyAsA==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.45.0': - resolution: {integrity: sha512-IaX3ZX1A/0wlgWZue+1BNWlq5xtJgsRo7uUk/aSiYD7lPbJ7dFuZ+yTLFLKgbl4O0QcyHTj1/mSBj9ryF1Lizg==} + '@algolia/monitoring@1.46.0': + resolution: {integrity: sha512-uivZ9wSWZ8mz2ZU0dgDvQwvVZV8XBv6lYBXf8UtkQF3u7WeTqBPeU8ZoeTyLpf0jAXCYOvc1mAVmK0xPLuEwOQ==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.45.0': - resolution: {integrity: sha512-1jeMLoOhkgezCCPsOqkScwYzAAc1Jr5T2hisZl0s32D94ZV7d1OHozBukgOjf8Dw+6Hgi6j52jlAdUWTtkX9Mg==} + '@algolia/recommend@5.46.0': + resolution: {integrity: sha512-O2BB8DuySuddgOAbhyH4jsGbL+KyDGpzJRtkDZkv091OMomqIA78emhhMhX9d/nIRrzS1wNLWB/ix7Hb2eV5rg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.45.0': - resolution: {integrity: sha512-46FIoUkQ9N7wq4/YkHS5/W9Yjm4Ab+q5kfbahdyMpkBPJ7IBlwuNEGnWUZIQ6JfUZuJVojRujPRHMihX4awUMg==} + '@algolia/requester-browser-xhr@5.46.0': + resolution: {integrity: sha512-eW6xyHCyYrJD0Kjk9Mz33gQ40LfWiEA51JJTVfJy3yeoRSw/NXhAL81Pljpa0qslTs6+LO/5DYPZddct6HvISQ==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.45.0': - resolution: {integrity: sha512-XFTSAtCwy4HdBhSReN2rhSyH/nZOM3q3qe5ERG2FLbYId62heIlJBGVyAPRbltRwNlotlydbvSJ+SQ0ruWC2cw==} + '@algolia/requester-fetch@5.46.0': + resolution: {integrity: sha512-Vn2+TukMGHy4PIxmdvP667tN/MhS7MPT8EEvEhS6JyFLPx3weLcxSa1F9gVvrfHWCUJhLWoMVJVB2PT8YfRGcw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.45.0': - resolution: {integrity: sha512-8mTg6lHx5i44raCU52APsu0EqMsdm4+7Hch/e4ZsYZw0hzwkuaMFh826ngnkYf9XOl58nHoou63aZ874m8AbpQ==} + '@algolia/requester-node-http@5.46.0': + resolution: {integrity: sha512-xaqXyna5yBZ+r1SJ9my/DM6vfTqJg9FJgVydRJ0lnO+D5NhqGW/qaRG/iBGKr/d4fho34el6WakV7BqJvrl/HQ==} engines: {node: '>= 14.0.0'} '@alloc/quick-lru@5.2.0': @@ -1529,17 +1547,17 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect-cli@0.2101.0-next.0': - resolution: {integrity: sha512-HYhpi4uqImgSV95D//N9CPHQvupb2VNyAU1ozhNrBDwko+RhDuHGdrFhyqwqvPytsHYekPML0SZVBy+O8DVL7A==} + '@angular-devkit/architect-cli@0.2101.0-next.2': + resolution: {integrity: sha512-M4yv1zqCv5fIRjR4YIifavEfno7MhqewTz42ab7TPbyEpBguCyyvheOSjVRhXy1TjliiMSoxcckCaPpHGCjceg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/architect@0.2101.0-next.0': - resolution: {integrity: sha512-aExU6NQA1RDKqH9oecf+YBSZE7UlsdsWKqMJ8Me8Zj7PzMy6CNu82xL1Cv4yRB3xTHfNlB+CSnwnU6FoOoGohw==} + '@angular-devkit/architect@0.2101.0-next.2': + resolution: {integrity: sha512-Lm8VsC+FmZ29/iZ4fjyAqp+kFjzY+UPUtIAFcckux0ss63JxOWxBhgU3QL8EwvGCnaUuYO7XoBwG3fV9dNp0fA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/build-angular@21.1.0-next.0': - resolution: {integrity: sha512-WT1Bzm0KLS+5me7Cup5o0iM6JaPjU9yP8S4AVX54ikHEtuiw+dxiait7aEAapMlcO8Wt2m8FMC+X0RChReAtJA==} + '@angular-devkit/build-angular@21.1.0-next.2': + resolution: {integrity: sha512-fZvBzacz/NpSRvN0eSRzZXm5XIuZtd/FLosjsqcNcVM05pK1SxC7foHoW1nOQ9S4W4lq9inLpD4VNN4AZgH04A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -1548,7 +1566,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 '@web/test-runner': ^0.20.0 browser-sync: ^3.0.2 jest: ^30.2.0 @@ -1588,34 +1606,34 @@ packages: tailwindcss: optional: true - '@angular-devkit/build-webpack@0.2101.0-next.0': - resolution: {integrity: sha512-eLnDxsc3YfdLjl+0FPPunEjvmwdhmH28aiZIQxqN0ULH/iKercdjuI51ilwG/fS+UUpIxazXcyHqv9MjL0Sc/Q==} + '@angular-devkit/build-webpack@0.2101.0-next.2': + resolution: {integrity: sha512-lj2zbp9Xo2qPm3qGRswYsOzZ8ED9HExMTHt9pDDzRcVpnKCqQGSINmJVndnD8xLVt92S39Cpr1QBOsE6U9FKXQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - '@angular-devkit/core@21.1.0-next.0': - resolution: {integrity: sha512-a48yNgGp79R23HPekTjjcwgRrtJEDndN1nBs0j42qgg+jSM/cqeTw7rMjNPAgNv9dkqxn3Rk/G1WGKIEHANlkQ==} + '@angular-devkit/core@21.1.0-next.2': + resolution: {integrity: sha512-SavlqKJmM+EqX1eg4iuboaqbJTBRALuwuocctSBmowOoYFYjJXaJbdit1fwB0vqQ946RKG56YYCcAEuIhjmNwA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - chokidar: ^4.0.0 + chokidar: ^5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@21.1.0-next.0': - resolution: {integrity: sha512-fi8zVvjGkrW3DQTB8NEHvKVQpW62B1o3ZMazRYRTwxMlqJiWn+eCU3EzuVw1ScgxlvnHeacesxIn9OPovGFIug==} + '@angular-devkit/schematics@21.1.0-next.2': + resolution: {integrity: sha512-01krelZX9UC8l8BlkIpExQ8fIekvtnSk0831j9dbWxbhKu/QZa8IHbKsB14Ph6NNvlz84FmUFhZpODMwjS831w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular/aria@21.1.0-next.0': - resolution: {integrity: sha512-R5s9LjLvyBSxhKJ5cWkGIBL0S3x5c1Z1OQQzR39LYvOBtMlDOduVqE3ebHzeshfV68dmsMdYcz7NjCDQ7xevrQ==} + '@angular/aria@21.1.0-next.3': + resolution: {integrity: sha512-jJJgNaovjL5d6y34VW7++Zw2g3QCKq1zgW4r/FXvGWBec4BZ6vVacVDOY8YeymB151iF5M/0ZJuLVDV0jCN7LQ==} peerDependencies: - '@angular/cdk': 21.1.0-next.0 + '@angular/cdk': 21.1.0-next.3 '@angular/core': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 - '@angular/build@21.1.0-next.0': - resolution: {integrity: sha512-2vH+9Caw75JnviZ97mibOLPaTwJgHxjVuYoE9za5Arm9Up4NsCakui//V379qkLobsEAnln0huh8g0PC+mJ4Jg==} + '@angular/build@21.1.0-next.2': + resolution: {integrity: sha512-WHbkVNH5t+OyOd3ciWKxMhAe9IdTGMqUxH+HasiJ+MYcwQ4SCqL4GWsOPl+UiJrOUKa4At9yd5lnOpjz1kWrWA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^21.0.0 || ^21.1.0-next.0 @@ -1625,7 +1643,7 @@ packages: '@angular/platform-browser': ^21.0.0 || ^21.1.0-next.0 '@angular/platform-server': ^21.0.0 || ^21.1.0-next.0 '@angular/service-worker': ^21.0.0 || ^21.1.0-next.0 - '@angular/ssr': ^21.1.0-next.0 + '@angular/ssr': ^21.1.0-next.2 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 || ^21.1.0-next.0 @@ -1660,15 +1678,16 @@ packages: vitest: optional: true - '@angular/cdk@21.1.0-next.0': - resolution: {integrity: sha512-Rao5QbCStbeb9SfWT1SvZCo6nPtdRo5IljrLwrWW1cKZruE4suVSoXS3JGom3jVphxxD7pwk+NZg4eER3vCRrw==} + '@angular/cdk@21.1.0-next.3': + resolution: {integrity: sha512-1NxzybXwBefUdOX5HzffjgZg4AwYuogDfRDgViTSzM4yZsVPup5+dDafwZAjYu90qdjriH5d/Lf6PUxhp2rLtA==} peerDependencies: '@angular/common': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 '@angular/core': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 + '@angular/platform-browser': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/cli@21.1.0-next.0': - resolution: {integrity: sha512-QpboTD5qCFjw1SRPjnVWJQ7c2s4Co5rnHSvVWuAixtM4rlNkulzgzcAndO4HqjnBq4Q8RRs8V1L/JeW71MSfwQ==} + '@angular/cli@21.1.0-next.2': + resolution: {integrity: sha512-SLBX5G187ih0gu+SoXoQ6wPJcmbZtVwjD705YEPH4YD1vsv1IP8YOwGlIV5SHbv7A4cM+wL1S1HNHD/e950iJQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true @@ -1678,26 +1697,26 @@ packages: peerDependencies: '@angular/core': 18.2.10 - '@angular/common@21.1.0-next.0': - resolution: {integrity: sha512-GS4D+A4qx5ojAdedUTjQXs3vfq9rzi8ffX1X/fTpycYL2j2CArOU/Tv2A6pedDlZetJMu0iifcLNuZNE4+ReKw==} + '@angular/common@21.1.0-next.4': + resolution: {integrity: sha512-HNM0eaZ86pXQZnmI6MlVj0FvvI3wF5mBkGyMN8Ktuswf9DUq04xBkliLiMwkb5UFmeSibxE3mUaMymw92Nn4fA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.1.0-next.0 + '@angular/core': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.1.0-next.0': - resolution: {integrity: sha512-gaPkonFV0EPTiLLTcWC0hMYmlCYiG3R5TMxyaxqrMmcJrMqcI9eqJPqfD4zgnZqntS2IzJtzCx2PbZlj8eibpA==} + '@angular/compiler-cli@21.1.0-next.4': + resolution: {integrity: sha512-iW+8gnGSUqCv4WdN3LMv9ikh9vHfKnbfaG01Hvzxs+q4tL3xVRDezeL+EnpaIdmKsCOIfsYrWwAXNfMd48S4Lw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.1.0-next.0 + '@angular/compiler': 21.1.0-next.4 typescript: '>=5.9 <6.0' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.1.0-next.0': - resolution: {integrity: sha512-7RBOOws5enj6Cl63QPpFKEuXgnfUz+fVsSa2S772hVCarwPoV5p33hUYhSchGh54GkbU2lrQqyDXK6nUdUoWGw==} + '@angular/compiler@21.1.0-next.4': + resolution: {integrity: sha512-uY4Yg3OJ/DL6AlqMjO8VXgKiFHJK3QspFJzslkJKys2d8I7a7YIoWxYRJ9ZUfWW++8Swig17pL9NOrRLXx+iQg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@angular/core@18.2.10': @@ -1715,27 +1734,37 @@ packages: '@angular/compiler': optional: true + '@angular/core@21.1.0-next.4': + resolution: {integrity: sha512-aJAGd+8o/8vle68hAJGah/DMQVD4/vFf/lDhnqe69sFLY7HLeq5UdBjIu00nZ1DUVeL0n/QOA97bLRICINhVrg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@angular/compiler': 21.1.0-next.4 + rxjs: ^6.5.3 || ^7.4.0 + peerDependenciesMeta: + '@angular/compiler': + optional: true + '@angular/domino@https://codeload.github.com/angular/domino/tar.gz/93e720f143d0296dd2726ffbcf4fc12283363a7b': resolution: {tarball: https://codeload.github.com/angular/domino/tar.gz/93e720f143d0296dd2726ffbcf4fc12283363a7b} version: 2.1.6 - '@angular/material@21.1.0-next.0': - resolution: {integrity: sha512-HBz/7SwpmGqKi9paSJ44mEoTsI7UXKesl01Rg0hR2KzR5m16+oi1XKfV8061DXyX/+CyluqWByQjiChgQO3NVQ==} + '@angular/material@21.1.0-next.3': + resolution: {integrity: sha512-m59JnFOUpTk5yLAYpJnk+nfvhzUO7tIG/WHFFOD2VmqWuadyZ+k6M4bQPy0ereumUcLue1QN7ZM6UpJWlgRqVQ==} peerDependencies: - '@angular/cdk': 21.1.0-next.0 + '@angular/cdk': 21.1.0-next.3 '@angular/common': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 '@angular/core': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 '@angular/forms': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 '@angular/platform-browser': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23': - resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23} - version: 0.0.0-95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/24c98502339594196a800db33dd4294e359ea952': + resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/24c98502339594196a800db33dd4294e359ea952} + version: 0.0.0-dd5658bd720370542913e23b21d80450bac94b60 hasBin: true - '@angular/ssr@21.1.0-next.0': - resolution: {integrity: sha512-8sit1MbJq0wc/VgSs7Rd2yRUiLV/WFGi3vZgJ9A07s9nuRy/asB4OHgxVb3avINlGp+XaifbUONWRqy65XbwZg==} + '@angular/ssr@21.1.0-next.2': + resolution: {integrity: sha512-WYORIbnS5wkp/sVosb4dsmu96lg/x2hdKE8oBag5/3RfLp2xjc1W0wY3XAAF+YJyiKIJG24X487zwiArzq0B9w==} peerDependencies: '@angular/common': ^21.0.0 || ^21.1.0-next.0 '@angular/core': ^21.0.0 || ^21.1.0-next.0 @@ -1773,6 +1802,9 @@ packages: '@asamuzakjp/dom-selector@6.7.4': resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==} + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -2446,8 +2478,8 @@ packages: resolution: {integrity: sha512-eZ/Aq+2r4PcJa6LbPCT6ffgIJfTU/gYilqIzoX2OLM4nNkbQC6tTMPZNn7aHHjhGPxbNLv41zm4Xqt1olCLgXw==} hasBin: true - '@bazel/ibazel@0.27.0': - resolution: {integrity: sha512-nIQPXoBMKMTD9DzQqBZMuYPRrxiu8Iouw7lAc+gR0WIJOG/NX7qyemPECDEAbVP3mAJKmzyCIQbhYFFx4DjOOg==} + '@bazel/ibazel@0.28.0': + resolution: {integrity: sha512-8WV3Uulai7afTG0OLo7Kf72actI0N6WK4a89i0kClO0WVlY2DguKyBPR/Xs//Sn/U4c4V93b0HS1ufUv3aTxbA==} hasBin: true '@bazel/runfiles@6.5.0': @@ -2507,8 +2539,8 @@ packages: '@codemirror/state@6.5.2': resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} - '@codemirror/view@6.38.8': - resolution: {integrity: sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==} + '@codemirror/view@6.39.4': + resolution: {integrity: sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} @@ -2570,6 +2602,12 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-syntax-patches-for-csstree@1.0.14': + resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + '@csstools/css-syntax-patches-for-csstree@1.0.17': resolution: {integrity: sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A==} engines: {node: '>=18'} @@ -2627,6 +2665,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -2639,6 +2683,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -2651,6 +2701,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -2663,6 +2719,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -2675,6 +2737,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -2687,6 +2755,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -2699,6 +2773,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -2711,6 +2791,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -2723,6 +2809,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -2735,6 +2827,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -2747,6 +2845,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -2759,6 +2863,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -2771,6 +2881,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -2783,6 +2899,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -2795,6 +2917,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -2807,6 +2935,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -2819,6 +2953,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -2831,6 +2971,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -2843,6 +2989,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -2855,6 +3007,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -2867,6 +3025,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} @@ -2879,6 +3043,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -2891,6 +3061,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -2903,6 +3079,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -2915,6 +3097,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -2927,6 +3115,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@externs/nodejs@1.5.0': resolution: {integrity: sha512-2J+FRDjGfKKldTQ5YqLmhQ04/sA9swTQ6OTtPzo4xhg41u1+eiufciXiqW6u3UXEcmm413a27NakgnLbzfp0wQ==} @@ -2934,8 +3128,8 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@firebase/ai@2.6.0': - resolution: {integrity: sha512-NGyE7NQDFznOv683Xk4+WoUv39iipa9lEfrwvvPz33ChzVbCCiB69FJQTK2BI/11pRtzYGbHo1/xMz7gxWWhJw==} + '@firebase/ai@2.6.1': + resolution: {integrity: sha512-qJd9bpABqsanFnwdbjZEDbKKr1jRtuUZ+cHyNBLWsxobH4pd73QncvuO3XlMq4eKBLlg1f5jNdFpJ3G3ABu2Tg==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -2983,8 +3177,8 @@ packages: resolution: {integrity: sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==} engines: {node: '>=20.0.0'} - '@firebase/auth-compat@0.6.1': - resolution: {integrity: sha512-I0o2ZiZMnMTOQfqT22ur+zcGDVSAfdNZBHo26/Tfi8EllfR1BO7aTVo2rt/ts8o/FWsK8pOALLeVBGhZt8w/vg==} + '@firebase/auth-compat@0.6.2': + resolution: {integrity: sha512-8UhCzF6pav9bw/eXA8Zy1QAKssPRYEYXaWagie1ewLTwHkXv6bKp/j6/IwzSYQP67sy/BMFXIFaCCsoXzFLr7A==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app-compat': 0.x @@ -2998,12 +3192,12 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/auth@1.11.1': - resolution: {integrity: sha512-Mea0G/BwC1D0voSG+60Ylu3KZchXAFilXQ/hJXWCw3gebAu+RDINZA0dJMNeym7HFxBaBaByX8jSa7ys5+F2VA==} + '@firebase/auth@1.12.0': + resolution: {integrity: sha512-zkvLpsrxynWHk07qGrUDfCSqKf4AvfZGEqJ7mVCtYGjNNDbGE71k0Yn84rg8QEZu4hQw1BC0qDEHzpNVBcSVmA==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x - '@react-native-async-storage/async-storage': ^1.18.1 + '@react-native-async-storage/async-storage': ^2.2.0 peerDependenciesMeta: '@react-native-async-storage/async-storage': optional: true @@ -3028,8 +3222,8 @@ packages: resolution: {integrity: sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==} engines: {node: '>=20.0.0'} - '@firebase/firestore-compat@0.4.2': - resolution: {integrity: sha512-cy7ov6SpFBx+PHwFdOOjbI7kH00uNKmIFurAn560WiPCZXy9EMnil1SOG7VF4hHZKdenC+AHtL4r3fNpirpm0w==} + '@firebase/firestore-compat@0.4.3': + resolution: {integrity: sha512-1ylF/njF68Pmb6p0erP0U78XQv1w77Wap4bUmqZ7ZVkmN1oMgplyu0TyirWtCBoKFRV2+SUZfWXvIij/z39LYg==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app-compat': 0.x @@ -3040,8 +3234,8 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/firestore@4.9.2': - resolution: {integrity: sha512-iuA5+nVr/IV/Thm0Luoqf2mERUvK9g791FZpUJV1ZGXO6RL2/i/WFJUj5ZTVXy5pRjpWYO+ZzPcReNrlilmztA==} + '@firebase/firestore@4.9.3': + resolution: {integrity: sha512-RVuvhcQzs1sD5Osr2naQS71H0bQMbSnib16uOWAKk3GaKb/WBPyCYSr2Ry7MqlxDP/YhwknUxECL07lw9Rq1nA==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -3184,11 +3378,11 @@ packages: resolution: {integrity: sha512-IJn+8A3QZJfe7FUtWqHVNo3xJs7KFpurCWGWCiCz3oEh+BkRymKZ1QxfAbU2yGMDzTytLGQ2IV6T2r3cuo75/w==} engines: {node: '>=18'} - '@google/genai@1.30.0': - resolution: {integrity: sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w==} + '@google/genai@1.33.0': + resolution: {integrity: sha512-ThUjFZ1N0DU88peFjnQkb8K198EWaW2RmmnDShFQ+O+xkIH9itjpRe358x3L/b4X/A7dimkvq63oz49Vbh7Cog==} engines: {node: '>=20.0.0'} peerDependencies: - '@modelcontextprotocol/sdk': ^1.20.1 + '@modelcontextprotocol/sdk': ^1.24.0 peerDependenciesMeta: '@modelcontextprotocol/sdk': optional: true @@ -3241,6 +3435,10 @@ packages: resolution: {integrity: sha512-QAZUk6BBncv/XmSEZTscd8qazzjV3E0leUMrEPjxCd51QBgCKmprUGLex5DTsNtURm7LMzv+CLcd6S86xvBfYg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/ansi@2.0.2': + resolution: {integrity: sha512-SYLX05PwJVnW+WVegZt1T4Ip1qba1ik+pNJPDiqvk6zS5Y/i8PhRzLpGEtVd7sW0G8cMtkD8t4AZYhQwm8vnww==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/checkbox@4.3.2': resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} engines: {node: '>=18'} @@ -3259,6 +3457,15 @@ packages: '@types/node': optional: true + '@inquirer/checkbox@5.0.3': + resolution: {integrity: sha512-xtQP2eXMFlOcAhZ4ReKP2KZvDIBb1AnCfZ81wWXG3DXLVH0f0g4obE0XDPH+ukAEMRcZT0kdX2AS1jrWGXbpxw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/confirm@5.1.21': resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} @@ -3277,6 +3484,15 @@ packages: '@types/node': optional: true + '@inquirer/confirm@6.0.3': + resolution: {integrity: sha512-lyEvibDFL+NA5R4xl8FUmNhmu81B+LDL9L/MpKkZlQDJZXzG8InxiqYxiAlQYa9cqLLhYqKLQwZqXmSTqCLjyw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/core@10.3.2': resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} @@ -3295,6 +3511,15 @@ packages: '@types/node': optional: true + '@inquirer/core@11.1.0': + resolution: {integrity: sha512-+jD/34T1pK8M5QmZD/ENhOfXdl9Zr+BrQAUc5h2anWgi7gggRq15ZbiBeLoObj0TLbdgW7TAIQRU2boMc9uOKQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/editor@4.2.23': resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} engines: {node: '>=18'} @@ -3313,6 +3538,15 @@ packages: '@types/node': optional: true + '@inquirer/editor@5.0.3': + resolution: {integrity: sha512-wYyQo96TsAqIciP/r5D3cFeV8h4WqKQ/YOvTg5yOfP2sqEbVVpbxPpfV3LM5D0EP4zUI3EZVHyIUIllnoIa8OQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/expand@4.0.23': resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} engines: {node: '>=18'} @@ -3331,6 +3565,15 @@ packages: '@types/node': optional: true + '@inquirer/expand@5.0.3': + resolution: {integrity: sha512-2oINvuL27ujjxd95f6K2K909uZOU2x1WiAl7Wb1X/xOtL8CgQ1kSxzykIr7u4xTkXkXOAkCuF45T588/YKee7w==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -3349,6 +3592,15 @@ packages: '@types/node': optional: true + '@inquirer/external-editor@2.0.2': + resolution: {integrity: sha512-X/fMXK7vXomRWEex1j8mnj7s1mpnTeP4CO/h2gysJhHLT2WjBnLv4ZQEGpm/kcYI8QfLZ2fgW+9kTKD+jeopLg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/figures@1.0.15': resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} @@ -3357,6 +3609,10 @@ packages: resolution: {integrity: sha512-KtMxyjLCuDFqAWHmCY9qMtsZ09HnjMsm8H3OvpSIpfhHdfw3/AiGWHNrfRwbyvHPtOJpumm8wGn5fkhtvkWRsg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/figures@2.0.2': + resolution: {integrity: sha512-qXm6EVvQx/FmnSrCWCIGtMHwqeLgxABP8XgcaAoywsL0NFga9gD5kfG0gXiv80GjK9Hsoz4pgGwF/+CjygyV9A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/input@4.3.1': resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} engines: {node: '>=18'} @@ -3375,6 +3631,15 @@ packages: '@types/node': optional: true + '@inquirer/input@5.0.3': + resolution: {integrity: sha512-4R0TdWl53dtp79Vs6Df2OHAtA2FVNqya1hND1f5wjHWxZJxwDMSNB1X5ADZJSsQKYAJ5JHCTO+GpJZ42mK0Otw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/number@3.0.23': resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} engines: {node: '>=18'} @@ -3393,6 +3658,15 @@ packages: '@types/node': optional: true + '@inquirer/number@4.0.3': + resolution: {integrity: sha512-TjQLe93GGo5snRlu83JxE38ZPqj5ZVggL+QqqAF2oBA5JOJoxx25GG3EGH/XN/Os5WOmKfO8iLVdCXQxXRZIMQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/password@4.0.23': resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} engines: {node: '>=18'} @@ -3411,6 +3685,15 @@ packages: '@types/node': optional: true + '@inquirer/password@5.0.3': + resolution: {integrity: sha512-rCozGbUMAHedTeYWEN8sgZH4lRCdgG/WinFkit6ZPsp8JaNg2T0g3QslPBS5XbpORyKP/I+xyBO81kFEvhBmjA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/prompts@7.10.1': resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} engines: {node: '>=18'} @@ -3429,6 +3712,15 @@ packages: '@types/node': optional: true + '@inquirer/prompts@8.1.0': + resolution: {integrity: sha512-LsZMdKcmRNF5LyTRuZE5nWeOjganzmN3zwbtNfcs6GPh3I2TsTtF1UYZlbxVfhxd+EuUqLGs/Lm3Xt4v6Az1wA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/rawlist@4.1.11': resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} engines: {node: '>=18'} @@ -3447,6 +3739,15 @@ packages: '@types/node': optional: true + '@inquirer/rawlist@5.1.0': + resolution: {integrity: sha512-yUCuVh0jW026Gr2tZlG3kHignxcrLKDR3KBp+eUgNz+BAdSeZk0e18yt2gyBr+giYhj/WSIHCmPDOgp1mT2niQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/search@3.2.2': resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} engines: {node: '>=18'} @@ -3465,6 +3766,15 @@ packages: '@types/node': optional: true + '@inquirer/search@4.0.3': + resolution: {integrity: sha512-lzqVw0YwuKYetk5VwJ81Ba+dyVlhseHPx9YnRKQgwXdFS0kEavCz2gngnNhnMIxg8+j1N/rUl1t5s1npwa7bqg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/select@4.4.2': resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} engines: {node: '>=18'} @@ -3483,6 +3793,15 @@ packages: '@types/node': optional: true + '@inquirer/select@5.0.3': + resolution: {integrity: sha512-M+ynbwS0ecQFDYMFrQrybA0qL8DV0snpc4kKevCCNaTpfghsRowRY7SlQBeIYNzHqXtiiz4RG9vTOeb/udew7w==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/type@3.0.10': resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} @@ -3501,6 +3820,15 @@ packages: '@types/node': optional: true + '@inquirer/type@4.0.2': + resolution: {integrity: sha512-cae7mzluplsjSdgFA6ACLygb5jC8alO0UUnFPyu0E7tNRPrL+q/f8VcSXp+cjZQ7l5CMpDpi2G1+IQvkOiL1Lw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -3684,8 +4012,8 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@lezer/common@1.3.0': - resolution: {integrity: sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==} + '@lezer/common@1.4.0': + resolution: {integrity: sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==} '@lezer/css@1.3.0': resolution: {integrity: sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==} @@ -3699,8 +4027,8 @@ packages: '@lezer/javascript@1.5.4': resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - '@lezer/lr@1.4.3': - resolution: {integrity: sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA==} + '@lezer/lr@1.4.5': + resolution: {integrity: sha512-/YTRKP5yPPSo1xImYQk7AZZMAgap0kegzqCSYHjAL9x1AZ0ZQW+IpcEzMKagCsbTsLnVeWkxYrCNeXG8xEPrjg==} '@lezer/sass@1.1.0': resolution: {integrity: sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==} @@ -3766,8 +4094,8 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - '@modelcontextprotocol/sdk@1.22.0': - resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} + '@modelcontextprotocol/sdk@1.24.3': + resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -3925,16 +4253,16 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} '@nginfra/angular-linking@1.0.10': resolution: {integrity: sha512-31zx+PCN8tBlC0FYUuCxS4uVPJLAlBhi4UVp6QgoQG44RsOHKTmcRORMVSJdPLRgRuCJkY45kj+PE3AxsgiUKA==} peerDependencies: '@angular/compiler-cli': '*' - '@ngtools/webpack@21.1.0-next.0': - resolution: {integrity: sha512-lFlrmQARJJKwlYfoGGXKrr7Bbk5FJzx6xCEgw6ZqnH5FjIaxHrXZ+jLmoEeqUKeX0NSuX5q0oLpE40IExFh2FA==} + '@ngtools/webpack@21.1.0-next.2': + resolution: {integrity: sha512-EFou/qczcYYaKFunyl4iLp6tJqClwO2lngesO6eDRlV2LdRNlB0uImmurNyluaM3vKkLvRkRVlzq0G4Q6kQTUw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^21.0.0 || ^21.1.0-next.0 @@ -4175,8 +4503,8 @@ packages: resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} engines: {node: '>=14'} - '@oxc-project/types@0.98.0': - resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==} + '@oxc-project/types@0.101.0': + resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==} '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} @@ -4286,8 +4614,8 @@ packages: resolution: {integrity: sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==} engines: {node: '>=18.12'} - '@pnpm/dependency-path@1001.1.5': - resolution: {integrity: sha512-powgYgNzuAdrZK+bx1Vxes5LRFp8ByUCcFsCeo0pQpyFbKpRDFF31FUVSE3CGs61WgL0lTBQr7ZoUSRc+BDrCw==} + '@pnpm/dependency-path@1001.1.8': + resolution: {integrity: sha512-+/SabdOsq4ycO/s1F82mUTmYb9KTE7e74qbXE9caM6slbaJesVqQOKDxSP4RqCy5jkjDz26kpkWzxeNJLowdNQ==} engines: {node: '>=18.12'} '@pnpm/graceful-fs@1000.0.1': @@ -4302,8 +4630,8 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} - '@pnpm/types@1001.0.1': - resolution: {integrity: sha512-v5X09E6LkJFOOw9FgGITpAs7nQJtx6u3N0SNtyIC5mSeIC5SebMrrelpCz6QUTJvyXBEa1AWj2dZhYfLj59xhA==} + '@pnpm/types@1001.2.0': + resolution: {integrity: sha512-UIju+OadUVS0q5q/MbRAzMS5M9HZcZyT6evyrgPUH0DV9przkcW7/LH1Sj33Q2MpJO9Nzqw4b4w72x8mvtUAew==} engines: {node: '>=18.12'} '@protobufjs/aspromise@1.1.2': @@ -4341,95 +4669,89 @@ packages: engines: {node: '>=18'} hasBin: true - '@rolldown/binding-android-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==} + '@rolldown/binding-android-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.51': - resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==} + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': - resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': - resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': - resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': - resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': - resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': - resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': - resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.51': - resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==} + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/plugin-babel@6.1.0': resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} @@ -4494,8 +4816,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + '@rollup/rollup-android-arm-eabi@4.53.5': + resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==} cpu: [arm] os: [android] @@ -4504,8 +4826,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + '@rollup/rollup-android-arm64@4.53.5': + resolution: {integrity: sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==} cpu: [arm64] os: [android] @@ -4514,8 +4836,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + '@rollup/rollup-darwin-arm64@4.53.5': + resolution: {integrity: sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==} cpu: [arm64] os: [darwin] @@ -4524,8 +4846,8 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + '@rollup/rollup-darwin-x64@4.53.5': + resolution: {integrity: sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==} cpu: [x64] os: [darwin] @@ -4534,8 +4856,8 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + '@rollup/rollup-freebsd-arm64@4.53.5': + resolution: {integrity: sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==} cpu: [arm64] os: [freebsd] @@ -4544,8 +4866,8 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + '@rollup/rollup-freebsd-x64@4.53.5': + resolution: {integrity: sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==} cpu: [x64] os: [freebsd] @@ -4555,8 +4877,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': + resolution: {integrity: sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==} cpu: [arm] os: [linux] libc: [glibc] @@ -4567,8 +4889,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + '@rollup/rollup-linux-arm-musleabihf@4.53.5': + resolution: {integrity: sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==} cpu: [arm] os: [linux] libc: [musl] @@ -4579,8 +4901,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + '@rollup/rollup-linux-arm64-gnu@4.53.5': + resolution: {integrity: sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==} cpu: [arm64] os: [linux] libc: [glibc] @@ -4591,8 +4913,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + '@rollup/rollup-linux-arm64-musl@4.53.5': + resolution: {integrity: sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==} cpu: [arm64] os: [linux] libc: [musl] @@ -4603,8 +4925,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + '@rollup/rollup-linux-loong64-gnu@4.53.5': + resolution: {integrity: sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==} cpu: [loong64] os: [linux] libc: [glibc] @@ -4615,8 +4937,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + '@rollup/rollup-linux-ppc64-gnu@4.53.5': + resolution: {integrity: sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==} cpu: [ppc64] os: [linux] libc: [glibc] @@ -4627,8 +4949,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + '@rollup/rollup-linux-riscv64-gnu@4.53.5': + resolution: {integrity: sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -4639,8 +4961,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + '@rollup/rollup-linux-riscv64-musl@4.53.5': + resolution: {integrity: sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==} cpu: [riscv64] os: [linux] libc: [musl] @@ -4651,8 +4973,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + '@rollup/rollup-linux-s390x-gnu@4.53.5': + resolution: {integrity: sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==} cpu: [s390x] os: [linux] libc: [glibc] @@ -4663,8 +4985,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + '@rollup/rollup-linux-x64-gnu@4.53.5': + resolution: {integrity: sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==} cpu: [x64] os: [linux] libc: [glibc] @@ -4675,8 +4997,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + '@rollup/rollup-linux-x64-musl@4.53.5': + resolution: {integrity: sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==} cpu: [x64] os: [linux] libc: [musl] @@ -4686,8 +5008,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + '@rollup/rollup-openharmony-arm64@4.53.5': + resolution: {integrity: sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==} cpu: [arm64] os: [openharmony] @@ -4696,8 +5018,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + '@rollup/rollup-win32-arm64-msvc@4.53.5': + resolution: {integrity: sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==} cpu: [arm64] os: [win32] @@ -4706,8 +5028,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + '@rollup/rollup-win32-ia32-msvc@4.53.5': + resolution: {integrity: sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==} cpu: [ia32] os: [win32] @@ -4716,8 +5038,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + '@rollup/rollup-win32-x64-gnu@4.53.5': + resolution: {integrity: sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==} cpu: [x64] os: [win32] @@ -4726,8 +5048,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + '@rollup/rollup-win32-x64-msvc@4.53.5': + resolution: {integrity: sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==} cpu: [x64] os: [win32] @@ -4766,8 +5088,8 @@ packages: '@rushstack/ts-command-line@5.1.4': resolution: {integrity: sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA==} - '@schematics/angular@21.1.0-next.0': - resolution: {integrity: sha512-TBSh8b8+OdlgWkJlTTv/JRwrPTRH+mpJ32AlKRBimDChPDVP/+MAKlF7tsSbXKsi+V9mVWuBXcVR1A0R87mjkw==} + '@schematics/angular@21.1.0-next.2': + resolution: {integrity: sha512-NqVq5MbwNhJ5Phmv1pzj8ZvIVoGi3e4a702VbBlS6KxoZI9j8ulBh8ZnAWN3q6ZkSRnOIuPqvgowfeNbRtplFg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@secretlint/config-creator@10.2.2': @@ -4818,21 +5140,39 @@ packages: '@shikijs/core@3.15.0': resolution: {integrity: sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg==} + '@shikijs/core@3.20.0': + resolution: {integrity: sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g==} + '@shikijs/engine-javascript@3.15.0': resolution: {integrity: sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg==} + '@shikijs/engine-javascript@3.20.0': + resolution: {integrity: sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg==} + '@shikijs/engine-oniguruma@3.15.0': resolution: {integrity: sha512-HnqFsV11skAHvOArMZdLBZZApRSYS4LSztk2K3016Y9VCyZISnlYUYsL2hzlS7tPqKHvNqmI5JSUJZprXloMvA==} + '@shikijs/engine-oniguruma@3.20.0': + resolution: {integrity: sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==} + '@shikijs/langs@3.15.0': resolution: {integrity: sha512-WpRvEFvkVvO65uKYW4Rzxs+IG0gToyM8SARQMtGGsH4GDMNZrr60qdggXrFOsdfOVssG/QQGEl3FnJ3EZ+8w8A==} + '@shikijs/langs@3.20.0': + resolution: {integrity: sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==} + '@shikijs/themes@3.15.0': resolution: {integrity: sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ==} + '@shikijs/themes@3.20.0': + resolution: {integrity: sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==} + '@shikijs/types@3.15.0': resolution: {integrity: sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==} + '@shikijs/types@3.20.0': + resolution: {integrity: sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==} + '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -5213,6 +5553,9 @@ packages: '@types/node@24.10.1': resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.10.4': + resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -5718,8 +6061,8 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@5.45.0: - resolution: {integrity: sha512-wrj4FGr14heLOYkBKV3Fbq5ZBGuIFeDJkTilYq/G+hH1CSlQBtYvG2X1j67flwv0fUeQJwnWxxRIunSemAZirA==} + algoliasearch@5.46.0: + resolution: {integrity: sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==} engines: {node: '>= 14.0.0'} angular-mocks@1.5.11: @@ -5967,6 +6310,13 @@ packages: peerDependencies: postcss: ^8.1.0 + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -6091,6 +6441,10 @@ packages: resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} hasBin: true + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} + hasBin: true + basic-auth-connect@1.1.0: resolution: {integrity: sha512-rKcWjfiRZ3p5WS9e5q6msXa07s6DaFAMXoyowV+mb2xQG+oYdw2QEUyKi0Xp95JvXzShlM+oGy5QuqSK6TfC1Q==} @@ -6204,6 +6558,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + browserstack@1.6.1: resolution: {integrity: sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==} @@ -6311,6 +6670,9 @@ packages: caniuse-lite@1.0.30001756: resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -6385,13 +6747,13 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -6422,8 +6784,8 @@ packages: resolution: {integrity: sha512-yKNcXi/Mvi5kb1uK0sahubYiyfUO2EUgOp4NcY9+8NX5Xmc+4yeNogZuLFkpLBBj7/QI9MjRUIuXrV9XOw5kVg==} engines: {node: '>= 0.3.0'} - cldr@7.9.0: - resolution: {integrity: sha512-fnIifk57sudBE5kJTqvAGxhe5hxnYNPl338y6AYd9DhVuleg5QtCgqlKCu7joOqZATK8FsHJmaS6ysV/IlBzjQ==} + cldr@8.0.0: + resolution: {integrity: sha512-wRq8XBgmVemoq5jJeSWw8Sq1TdsVx02ZZWvfOAio3OEPjkstGprBHTMC3DxI1tmdd7CjS826lIOsdfgJHMu2lg==} cldrjs@0.5.5: resolution: {integrity: sha512-KDwzwbmLIPfCgd8JERVDpQKrUUM1U4KpFJJg2IROv89rF172lLufoJnqJ/Wea6fXL5bO6WjuLMzY8V52UWPvkA==} @@ -6904,14 +7266,18 @@ packages: resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==} engines: {node: '>=20'} + cssstyle@5.3.4: + resolution: {integrity: sha512-KyOS/kJMEq5O9GdPnaf82noigg5X5DYn0kZPJTaAsCUaBizp6Xa1y9D4Qoqf/JazEXWuruErHgVXwjN5391ZJw==} + engines: {node: '>=20'} + csv-parse@5.6.0: resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} custom-event@1.0.1: resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} - cypress@15.7.0: - resolution: {integrity: sha512-1C81zKxnQckYm2XGi37rPV4rN0bzUoWhydhKdOyshJn5gJKszEx5as9VLSZI0jp0ye49QxmnbU4TtMpcD+OmGQ==} + cypress@15.7.1: + resolution: {integrity: sha512-U3sYnJ+Cnpgr6IPycxsznTg//mGVXfPGeGV+om7VQCyp5XyVkhG4oPr3X3hTq1+OB0Om0O5DxusYmt7cbvwqMQ==} engines: {node: ^20.1.0 || ^22.0.0 || >=24.0.0} hasBin: true @@ -7403,6 +7769,9 @@ packages: electron-to-chromium@1.5.259: resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -7527,8 +7896,8 @@ packages: esbuild-plugin-umd-wrapper@3.0.0: resolution: {integrity: sha512-Ht3jrO7r8oF8FgPIhMnfNQvYLHhz2QA1ggPqfBhoVq0VfYNczqJ6nwQ4PhO5UvrUymn+Q0Nc50xpiDbxI+KR2Q==} - esbuild-wasm@0.27.0: - resolution: {integrity: sha512-4XpLDOY4sOzqgiezPXDkHTn25cBA1MKN5OTotehoFR7/wTpSYCK76OA8rQfpiuz12G27JpGxxdh+/D9GcNl61w==} + esbuild-wasm@0.27.1: + resolution: {integrity: sha512-NjueSyuuMjX6F/mnqQ8g4rkwAFTct7JT/A/oXjXhGTFbWiTm3zU59BMulOrT+KuCHj+oShTBARQCxCDDvVxwFA==} engines: {node: '>=18'} hasBin: true @@ -7542,6 +7911,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -7845,13 +8219,13 @@ packages: resolution: {integrity: sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==} engines: {node: '>= 10.13.0'} - firebase-tools@14.26.0: - resolution: {integrity: sha512-pjVNDvmY4s3HkBe0iI5mum4hGpNzVwrf0J68KP51XRvhU/tYDXzMr6N+FBjdJtkJM6I7wx1KqgHsqvJurCun/A==} + firebase-tools@15.0.0: + resolution: {integrity: sha512-6HSF3meQwMNzDK5BIEFEq4jVMPqR6PBdqpuSLBE+3zpLlcmLknMVBDhdN32jhd2lj/wvfHBZV6ZSqTCNF7AtWw==} engines: {node: '>=20.0.0 || >=22.0.0 || >=24.0.0'} hasBin: true - firebase@12.6.0: - resolution: {integrity: sha512-8ZD1Gcv916Qp8/nsFH2+QMIrfX/76ti6cJwxQUENLXXnKlOX/IJZaU2Y3bdYf5r1mbownrQKfnWtrt+MVgdwLA==} + firebase@12.7.0: + resolution: {integrity: sha512-ZBZg9jFo8uH4Emd7caOqtalKJfDGHnHQSrCPiqRAdTFQd0wL3ERilUBfhnhBLnlernugkN/o7nJa0p+sE71Izg==} flagged-respawn@2.0.0: resolution: {integrity: sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==} @@ -7949,10 +8323,6 @@ packages: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - fs-minipass@3.0.3: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8972,8 +9342,8 @@ packages: jasmine-core@4.6.1: resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} - jasmine-core@5.12.1: - resolution: {integrity: sha512-P/UbRZ0LKwXe7wEpwDheuhunPwITn4oPALhrJEQJo6756EwNGnsK/TSQrWojBB4cQDQ+VaxWYws9tFNDuiMh2Q==} + jasmine-core@5.13.0: + resolution: {integrity: sha512-vsYjfh7lyqvZX5QgqKc4YH8phs7g96Z8bsdIFNEU3VqXhlHaq+vov/Fgn/sr6MiUczdZkyXRC3TX369Ll4Nzbw==} jasmine-reporters@2.5.2: resolution: {integrity: sha512-qdewRUuFOSiWhiyWZX8Yx3YNQ9JG51ntBEO4ekLQRpktxFTwUHy24a86zD/Oi2BRTKksEdfWQZcQFqzjqIkPig==} @@ -8982,8 +9352,8 @@ packages: resolution: {integrity: sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==} hasBin: true - jasmine@5.12.0: - resolution: {integrity: sha512-KmKeTNuH8rgAuPRL5AUsXWSdJVlDu+pgqi2dLXoZUSH/g3kR+7Ho8B7hEhwDu0fu1PLuiXZtfaxmQ/mB5wqihw==} + jasmine@5.13.0: + resolution: {integrity: sha512-oLCXIhEb5e0zzjn9GyuvcuisvLBwUjmgz7a0RNGWKwQtJCDld4m+vwKUpAIJVLB5vbmQFdtKhT86/tIZlJ5gYw==} hasBin: true jasminewd2@2.2.0: @@ -9145,6 +9515,9 @@ packages: join-path@1.1.1: resolution: {integrity: sha512-jnt9OC34sLXMLJ6YfPQ2ZEKrR9mB5ZbSnQb4LPaOx1c5rTzxpR33L18jjp0r75mGGTJmsil3qwN1B5IBeTnSSA==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -9177,6 +9550,15 @@ packages: canvas: optional: true + jsdom@27.3.0: + resolution: {integrity: sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -9575,6 +9957,10 @@ packages: resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} engines: {node: 20 || >=22} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lru-cache@2.5.0: resolution: {integrity: sha512-dVmQmXPBlTgFw77hm60ud//l2bCuDKkqC2on1EBoM7s9Urm9IQDrnujwZ93NFnAq0dVZ0HBXTS7PwEG+YE7+EQ==} @@ -9713,6 +10099,9 @@ packages: mermaid@11.12.1: resolution: {integrity: sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g==} + mermaid@11.12.2: + resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} @@ -9845,18 +10234,10 @@ packages: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - minizlib@3.1.0: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} @@ -10600,8 +10981,8 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.56.1: - resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + playwright-core@1.57.0: + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} engines: {node: '>=18'} hasBin: true @@ -10741,13 +11122,13 @@ packages: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} - preact-render-to-string@6.6.3: - resolution: {integrity: sha512-7oHG7jzjriqsFPkSPiPnzrQ0GcxFm6wOkYWNdStK5Ks9YlWSQQXKGBRAX4nKDdqX7HAQuRvI4pZNZMycK4WwDw==} + preact-render-to-string@6.6.4: + resolution: {integrity: sha512-Bn6eQZ5SQ5loVEcC/mZmKT7HzO5Z/+vYzxfE/W2N468oSoNMJVdFGApF0GyXq0lDthuyXKTmtZ8k20NpYjr6Rw==} peerDependencies: preact: '>=10 || >= 11.0.0-0' - preact@10.27.2: - resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + preact@10.28.0: + resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==} prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} @@ -10759,6 +11140,11 @@ packages: engines: {node: '>=14'} hasBin: true + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@5.6.0: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} @@ -10991,6 +11377,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + rechoir@0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} @@ -11173,8 +11563,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rolldown@1.0.0-beta.51: - resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==} + rolldown@1.0.0-beta.53: + resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -11203,8 +11593,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + rollup@4.53.5: + resolution: {integrity: sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -11282,6 +11672,11 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + sass@1.95.0: + resolution: {integrity: sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==} + engines: {node: '>=14.0.0'} + hasBin: true + saucelabs@9.0.2: resolution: {integrity: sha512-37QGEOgp9BP1re6S06qpNcBZ0Hw+ZSkZkDepbXHT9VjYoRQwRzUoLtKqE4yyVeK7dzcQXQapmTGF1kp1jO2VDw==} hasBin: true @@ -11316,8 +11711,8 @@ packages: resolution: {integrity: sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==} engines: {node: '>= 6.9.0'} - selenium-webdriver@4.38.0: - resolution: {integrity: sha512-5/UXXFSQmn7FGQkbcpAqvfhzflUdMWtT7QqpEgkFD6Q6rDucxB5EUfzgjmr6JbUj30QodcW3mDXehzoeS/Vy5w==} + selenium-webdriver@4.39.0: + resolution: {integrity: sha512-NAs9jCU+UeZ/ZmRb8R6zOp7N8eMklefdBYASnaRmCNXdgFE8w3OCxxZmLixkwqnGDHY5VF7hCulfw1Mls43N/A==} engines: {node: '>= 20.0.0'} selfsigned@2.4.1: @@ -11436,6 +11831,9 @@ packages: shiki@3.15.0: resolution: {integrity: sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==} + shiki@3.20.0: + resolution: {integrity: sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -11856,8 +12254,8 @@ packages: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} - tailwindcss@3.4.18: - resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -11882,10 +12280,6 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - tar@7.5.2: resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} @@ -12172,6 +12566,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tuf-js@4.0.0: resolution: {integrity: sha512-Lq7ieeGvXDXwpoSmOSgLWVdsGGV9J4a77oDTAPe/Ltrqnnm/ETaRlBAQTH5JatEh8KXuE6sddf9qAv1Q2282Hg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -12425,6 +12824,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + update-notifier-cjs@5.1.7: resolution: {integrity: sha512-eZWTh8F+VCEoC4UIh0pKmh8h4izj65VvLhCpJpVefUxdYe0fU3GBrC4Sbh1AoWA/miNPAb6UVlp2fUQNsfp+3g==} engines: {node: '>=14'} @@ -12578,6 +12983,46 @@ packages: yaml: optional: true + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@4.0.13: resolution: {integrity: sha512-QSD4I0fN6uZQfftryIXuqvqgBxTvJ3ZNkF6RWECd82YGAYAfhcppBLFXzXJHQAAhVFyYEuFTrq6h0hQqjB7jIQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -12625,8 +13070,8 @@ packages: tslint: '*' typescript: '*' - vscode-html-languageservice@5.6.0: - resolution: {integrity: sha512-FIVz83oGw2tBkOr8gQPeiREInnineCKGCz3ZD1Pi6opOuX3nSRkc4y4zLLWsuop+6ttYX//XZCI6SLzGhRzLmA==} + vscode-html-languageservice@5.6.1: + resolution: {integrity: sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA==} vscode-jsonrpc@6.0.0: resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} @@ -13029,8 +13474,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true @@ -13127,15 +13572,26 @@ snapshots: '@acemir/cssom@0.9.24': {} - '@actions/core@1.11.1': + '@acemir/cssom@0.9.28': {} + + '@actions/core@2.0.0': dependencies: '@actions/exec': 1.1.1 - '@actions/http-client': 2.2.3 + '@actions/http-client': 3.0.0 + + '@actions/core@2.0.1': + dependencies: + '@actions/exec': 2.0.0 + '@actions/http-client': 3.0.0 '@actions/exec@1.1.1': dependencies: '@actions/io': 1.1.3 + '@actions/exec@2.0.0': + dependencies: + '@actions/io': 2.0.0 + '@actions/github@6.0.1': dependencies: '@actions/http-client': 2.2.3 @@ -13151,91 +13607,98 @@ snapshots: tunnel: 0.0.6 undici: 5.29.0 + '@actions/http-client@3.0.0': + dependencies: + tunnel: 0.0.6 + undici: 5.29.0 + '@actions/io@1.1.3': {} - '@algolia/abtesting@1.11.0': + '@actions/io@2.0.0': {} + + '@algolia/abtesting@1.12.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-abtesting@5.45.0': + '@algolia/client-abtesting@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-analytics@5.45.0': + '@algolia/client-analytics@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-common@5.45.0': {} + '@algolia/client-common@5.46.0': {} - '@algolia/client-insights@5.45.0': + '@algolia/client-insights@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-personalization@5.45.0': + '@algolia/client-personalization@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-query-suggestions@5.45.0': + '@algolia/client-query-suggestions@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-search@5.45.0': + '@algolia/client-search@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/ingestion@1.45.0': + '@algolia/ingestion@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/monitoring@1.45.0': + '@algolia/monitoring@1.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/recommend@5.45.0': + '@algolia/recommend@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/requester-browser-xhr@5.45.0': + '@algolia/requester-browser-xhr@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-fetch@5.45.0': + '@algolia/requester-fetch@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-node-http@5.45.0': + '@algolia/requester-node-http@5.46.0': dependencies: - '@algolia/client-common': 5.45.0 + '@algolia/client-common': 5.46.0 '@alloc/quick-lru@5.2.0': {} @@ -13244,30 +13707,30 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect-cli@0.2101.0-next.0(chokidar@4.0.3)': + '@angular-devkit/architect-cli@0.2101.0-next.2(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) ansi-colors: 4.1.3 progress: 2.0.3 yargs-parser: 22.0.0 transitivePeerDependencies: - chokidar - '@angular-devkit/architect@0.2101.0-next.0(chokidar@4.0.3)': + '@angular-devkit/architect@0.2101.0-next.2(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@21.1.0-next.0(c5bc0fdbba09cbd593e90489895b6169)': + '@angular-devkit/build-angular@21.1.0-next.2(66265e176ffea27481c8d22d24371267)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.0)))(webpack@5.103.0(esbuild@0.27.0)) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular/build': 21.1.0-next.0(703fb57f7d4265c06221da6c3dd7fb98) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) + '@angular-devkit/build-webpack': 0.2101.0-next.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.1)))(webpack@5.103.0(esbuild@0.27.1)) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) + '@angular/build': 21.1.0-next.2(598663e591c40f71412182159bdbc21c) '@angular/compiler-cli': link:packages/compiler-cli '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -13279,60 +13742,60 @@ snapshots: '@babel/preset-env': 7.28.5(@babel/core@7.28.5) '@babel/runtime': 7.28.4 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + '@ngtools/webpack': 21.1.0-next.2(@angular/compiler-cli@packages+compiler-cli)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.22(postcss@8.5.6) - babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)) + babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)) browserslist: 4.28.0 - copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.0)) - css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.0)) - esbuild-wasm: 0.27.0 + copy-webpack-plugin: 13.0.1(webpack@5.103.0(esbuild@0.27.1)) + css-loader: 7.1.2(webpack@5.103.0(esbuild@0.27.1)) + esbuild-wasm: 0.27.1 http-proxy-middleware: 3.0.5 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 less: 4.4.2 - less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)) - license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.0)) + less-loader: 12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)) + license-webpack-plugin: 4.0.2(webpack@5.103.0(esbuild@0.27.1)) loader-utils: 3.3.1 - mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.0)) + mini-css-extract-plugin: 2.9.4(webpack@5.103.0(esbuild@0.27.1)) open: 11.0.0 ora: 9.0.0 picomatch: 4.0.3 piscina: 5.1.4 postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)) resolve-url-loader: 5.0.0 rxjs: 7.8.2 - sass: 1.94.2 - sass-loader: 16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)) + sass: 1.95.0 + sass-loader: 16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)) semver: 7.7.3 - source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.0)) + source-map-loader: 5.0.0(webpack@5.103.0(esbuild@0.27.1)) source-map-support: 0.5.21 terser: 5.44.1 tinyglobby: 0.2.15 tree-kill: 1.2.2 tslib: 2.8.1 typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) - webpack-dev-middleware: 7.4.5(webpack@5.103.0(esbuild@0.27.0)) - webpack-dev-server: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.0)) + webpack: 5.103.0(esbuild@0.27.1) + webpack-dev-middleware: 7.4.5(webpack@5.103.0(esbuild@0.27.1)) + webpack-dev-server: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.1)) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.0)) + webpack-subresource-integrity: 5.1.0(webpack@5.103.0(esbuild@0.27.1)) optionalDependencies: '@angular/core': link:packages/core '@angular/localize': link:packages/localize '@angular/platform-browser': link:packages/platform-browser '@angular/platform-server': link:packages/platform-server '@angular/service-worker': link:packages/service-worker - '@angular/ssr': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) - esbuild: 0.27.0 + '@angular/ssr': 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + esbuild: 0.27.1 jest: 30.2.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) jest-environment-jsdom: 30.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) karma: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) - ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3) + ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.20.6)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3) protractor: 7.0.0 - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) + tailwindcss: 3.4.19(tsx@4.20.6)(yaml@2.8.2) transitivePeerDependencies: - '@angular/compiler' - '@rspack/core' @@ -13356,16 +13819,16 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.2101.0-next.0(chokidar@4.0.3)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.0)))(webpack@5.103.0(esbuild@0.27.0))': + '@angular-devkit/build-webpack@0.2101.0-next.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.1)))(webpack@5.103.0(esbuild@0.27.1))': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) rxjs: 7.8.2 - webpack: 5.103.0(esbuild@0.27.0) - webpack-dev-server: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.0)) + webpack: 5.103.0(esbuild@0.27.1) + webpack-dev-server: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.1)) transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/core@21.1.0-next.2(chokidar@5.0.0)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1 @@ -13374,11 +13837,11 @@ snapshots: rxjs: 7.8.2 source-map: 0.7.6 optionalDependencies: - chokidar: 4.0.3 + chokidar: 5.0.0 - '@angular-devkit/schematics@21.1.0-next.0(chokidar@4.0.3)': + '@angular-devkit/schematics@21.1.0-next.2(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.0.0 @@ -13386,26 +13849,26 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular/aria@21.1.0-next.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/core@packages+core)': + '@angular/aria@21.1.0-next.3(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/core@packages+core)': dependencies: - '@angular/cdk': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + '@angular/cdk': 21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/core': link:packages/core tslib: 2.8.1 - '@angular/build@21.1.0-next.0(2fee5caa4c7a4fbaa19a2f8f50e2bab6)': + '@angular/build@21.1.0-next.2(598663e591c40f71412182159bdbc21c)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) '@angular/compiler': link:packages/compiler '@angular/compiler-cli': link:packages/compiler-cli '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@inquirer/confirm': 5.1.21(@types/node@20.19.25) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2)) beasties: 0.3.5 browserslist: 4.28.0 - esbuild: 0.27.0 + esbuild: 0.27.1 https-proxy-agent: 7.0.6(supports-color@10.2.2) istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 @@ -13415,15 +13878,15 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.3 piscina: 5.1.4 - rolldown: 1.0.0-beta.51 - sass: 1.94.2 + rolldown: 1.0.0-beta.53 + sass: 1.95.0 semver: 7.7.3 source-map-support: 0.5.21 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 undici: 7.16.0 - vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) watchpack: 2.4.4 optionalDependencies: '@angular/core': link:packages/core @@ -13431,14 +13894,14 @@ snapshots: '@angular/platform-browser': link:packages/platform-browser '@angular/platform-server': link:packages/platform-server '@angular/service-worker': link:packages/service-worker - '@angular/ssr': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + '@angular/ssr': 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) karma: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) less: 4.4.2 lmdb: 3.4.4 - ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3) + ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.20.6)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3) postcss: 8.5.6 - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) - vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + tailwindcss: 3.4.19(tsx@4.20.6)(yaml@2.8.2) + vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@20.19.25)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - chokidar @@ -13452,20 +13915,20 @@ snapshots: - tsx - yaml - '@angular/build@21.1.0-next.0(703fb57f7d4265c06221da6c3dd7fb98)': + '@angular/build@21.1.0-next.2(94606abbceef2250d7cf197a30fc7802)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) '@angular/compiler': link:packages/compiler '@angular/compiler-cli': link:packages/compiler-cli '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@20.19.25) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@inquirer/confirm': 5.1.21(@types/node@24.10.4) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) beasties: 0.3.5 browserslist: 4.28.0 - esbuild: 0.27.0 + esbuild: 0.27.1 https-proxy-agent: 7.0.6(supports-color@10.2.2) istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 @@ -13475,15 +13938,75 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.3 piscina: 5.1.4 - rolldown: 1.0.0-beta.51 - sass: 1.94.2 + rolldown: 1.0.0-beta.53 + sass: 1.95.0 + semver: 7.7.3 + source-map-support: 0.5.21 + tinyglobby: 0.2.15 + tslib: 2.8.1 + typescript: 5.9.3 + undici: 7.16.0 + vite: 7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + watchpack: 2.4.4 + optionalDependencies: + '@angular/core': link:packages/core + '@angular/localize': link:packages/localize + '@angular/platform-browser': link:packages/platform-browser + '@angular/platform-server': link:packages/platform-server + '@angular/service-worker': link:packages/service-worker + '@angular/ssr': 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + karma: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + less: 4.4.2 + lmdb: 3.4.4 + ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3) + postcss: 8.5.6 + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@angular/build@21.1.0-next.2(e6f8c05c913935fcb8ae5b7a4b4218f6)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) + '@angular/compiler': link:packages/compiler + '@angular/compiler-cli': link:packages/compiler-cli + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-split-export-declaration': 7.24.7 + '@inquirer/confirm': 5.1.21(@types/node@24.10.4) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + beasties: 0.3.5 + browserslist: 4.28.0 + esbuild: 0.27.1 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + istanbul-lib-instrument: 6.0.3 + jsonc-parser: 3.3.1 + listr2: 9.0.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + parse5-html-rewriting-stream: 8.0.0 + picomatch: 4.0.3 + piscina: 5.1.4 + rolldown: 1.0.0-beta.53 + sass: 1.95.0 semver: 7.7.3 source-map-support: 0.5.21 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 undici: 7.16.0 - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) watchpack: 2.4.4 optionalDependencies: '@angular/core': link:packages/core @@ -13491,14 +14014,14 @@ snapshots: '@angular/platform-browser': link:packages/platform-browser '@angular/platform-server': link:packages/platform-server '@angular/service-worker': link:packages/service-worker - '@angular/ssr': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) + '@angular/ssr': 21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router) karma: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) less: 4.4.2 lmdb: 3.4.4 - ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3) + ng-packagr: 21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3) postcss: 8.5.6 - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) - vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@20.19.25)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - chokidar @@ -13512,25 +14035,26 @@ snapshots: - tsx - yaml - '@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2)': + '@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2)': dependencies: '@angular/common': link:packages/common '@angular/core': link:packages/core + '@angular/platform-browser': link:packages/platform-browser parse5: 8.0.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/cli@21.1.0-next.0(@types/node@20.19.25)(chokidar@4.0.3)': + '@angular/cli@21.1.0-next.2(@types/node@20.19.25)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) + '@angular-devkit/schematics': 21.1.0-next.2(chokidar@5.0.0) '@inquirer/prompts': 7.10.1(@types/node@20.19.25) '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.25))(@types/node@20.19.25)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.22.0 - '@schematics/angular': 21.1.0-next.0(chokidar@4.0.3) + '@modelcontextprotocol/sdk': 1.24.3 + '@schematics/angular': 21.1.0-next.2(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.45.0 + algoliasearch: 5.46.0 ini: 6.0.0 jsonc-parser: 3.3.1 listr2: 9.0.5 @@ -13540,24 +14064,24 @@ snapshots: resolve: 1.22.11 semver: 7.7.3 yargs: 18.0.0 - zod: 3.25.76 + zod: 4.1.13 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/cli@21.1.0-next.0(@types/node@24.10.1)(chokidar@4.0.3)': + '@angular/cli@21.1.0-next.2(@types/node@24.10.4)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2101.0-next.0(chokidar@4.0.3) - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.22.0 - '@schematics/angular': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/architect': 0.2101.0-next.2(chokidar@5.0.0) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) + '@angular-devkit/schematics': 21.1.0-next.2(chokidar@5.0.0) + '@inquirer/prompts': 7.10.1(@types/node@24.10.4) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.4))(@types/node@24.10.4)(listr2@9.0.5) + '@modelcontextprotocol/sdk': 1.24.3 + '@schematics/angular': 21.1.0-next.2(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.45.0 + algoliasearch: 5.46.0 ini: 6.0.0 jsonc-parser: 3.3.1 listr2: 9.0.5 @@ -13567,7 +14091,7 @@ snapshots: resolve: 1.22.11 semver: 7.7.3 yargs: 18.0.0 - zod: 3.25.76 + zod: 4.1.13 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' @@ -13580,18 +14104,18 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@angular/common@21.1.0-next.0(@angular/core@21.1.0-next.0(@angular/compiler@21.1.0-next.0))(rxjs@7.8.2)': + '@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.1.0-next.0(@angular/compiler@21.1.0-next.0) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.1.0-next.0(@angular/compiler@21.1.0-next.0)(typescript@5.9.3)': + '@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.1.0-next.0 + '@angular/compiler': 21.1.0-next.4 '@babel/core': 7.28.5 '@jridgewell/sourcemap-codec': 1.5.5 - chokidar: 4.0.3 + chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 semver: 7.7.3 @@ -13602,7 +14126,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.1.0-next.0': + '@angular/compiler@21.1.0-next.4': dependencies: tslib: 2.8.1 @@ -13612,19 +14136,27 @@ snapshots: tslib: 2.8.1 zone.js: 0.15.0 - '@angular/core@21.1.0-next.0(@angular/compiler@21.1.0-next.0)': + '@angular/core@21.1.0-next.0(@angular/compiler@packages+compiler)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 zone.js: 0.16.0 optionalDependencies: - '@angular/compiler': 21.1.0-next.0 + '@angular/compiler': link:packages/compiler + + '@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)': + dependencies: + rxjs: 7.8.2 + tslib: 2.8.1 + zone.js: 0.16.0 + optionalDependencies: + '@angular/compiler': 21.1.0-next.4 '@angular/domino@https://codeload.github.com/angular/domino/tar.gz/93e720f143d0296dd2726ffbcf4fc12283363a7b': {} - '@angular/material@21.1.0-next.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2)': + '@angular/material@21.1.0-next.3(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(@angular/forms@packages+forms)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2)': dependencies: - '@angular/cdk': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + '@angular/cdk': 21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/common': link:packages/common '@angular/core': link:packages/core '@angular/forms': link:packages/forms @@ -13632,13 +14164,13 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23(@modelcontextprotocol/sdk@1.22.0)': + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/24c98502339594196a800db33dd4294e359ea952(@modelcontextprotocol/sdk@1.24.3)': dependencies: - '@actions/core': 1.11.1 + '@actions/core': 2.0.1 '@google-cloud/spanner': 8.0.0(supports-color@10.2.2) - '@google/genai': 1.30.0(@modelcontextprotocol/sdk@1.22.0)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) - '@inquirer/prompts': 8.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@google/genai': 1.33.0(@modelcontextprotocol/sdk@1.24.3)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) + '@inquirer/prompts': 8.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) '@octokit/auth-app': 8.1.2 '@octokit/core': 7.0.6 '@octokit/graphql': 9.0.3 @@ -13649,14 +14181,14 @@ snapshots: '@octokit/request-error': 7.1.0 '@octokit/rest': 22.0.1 '@octokit/types': 16.0.0 - '@pnpm/dependency-path': 1001.1.5 + '@pnpm/dependency-path': 1001.1.8 '@types/cli-progress': 3.11.6 '@types/ejs': 3.1.5 '@types/events': 3.0.3 '@types/folder-hash': 4.0.4 '@types/git-raw-commits': 5.0.1 '@types/jasmine': 5.1.13 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/semver': 7.7.1 '@types/which': 3.0.4 '@types/yargs': 17.0.35 @@ -13669,11 +14201,11 @@ snapshots: ejs: 3.1.10 encoding: 0.1.13 fast-glob: 3.3.3 - firebase: 12.6.0 + firebase: 12.7.0 folder-hash: 4.1.1(supports-color@10.2.2) git-raw-commits: 5.0.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.2.1) - jasmine: 5.12.0 - jasmine-core: 5.12.1 + jasmine: 5.13.0 + jasmine-core: 5.13.0 jasmine-reporters: 2.5.2 jsonc-parser: 3.3.1 minimatch: 10.1.1 @@ -13681,18 +14213,18 @@ snapshots: nock: 14.0.10 semver: 7.7.3 supports-color: 10.2.2 - tsx: 4.20.6 + tsx: 4.21.0 typed-graphqlify: 3.1.6 typescript: 5.9.3 utf-8-validate: 6.0.5 which: 6.0.0 - yaml: 2.8.1 + yaml: 2.8.2 yargs: 18.0.0 transitivePeerDependencies: - '@modelcontextprotocol/sdk' - '@react-native-async-storage/async-storage' - '@angular/ssr@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router)': + '@angular/ssr@21.1.0-next.2(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router)': dependencies: '@angular/common': link:packages/common '@angular/core': link:packages/core @@ -13757,6 +14289,14 @@ snapshots: is-potential-custom-element-name: 1.0.1 lru-cache: 11.2.2 + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + '@asamuzakjp/nwsapi@2.3.9': {} '@azu/format-text@1.0.2': {} @@ -14659,7 +15199,7 @@ snapshots: '@bazel/buildifier@8.2.1': {} - '@bazel/ibazel@0.27.0': {} + '@bazel/ibazel@0.28.0': {} '@bazel/runfiles@6.5.0': {} @@ -14688,31 +15228,31 @@ snapshots: dependencies: '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 - '@lezer/common': 1.3.0 + '@codemirror/view': 6.39.4 + '@lezer/common': 1.4.0 '@codemirror/commands@6.10.0': dependencies: '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 - '@lezer/common': 1.3.0 + '@codemirror/view': 6.39.4 + '@lezer/common': 1.4.0 '@codemirror/lang-angular@0.1.4': dependencies: '@codemirror/lang-html': 6.4.11 '@codemirror/lang-javascript': 6.2.4 '@codemirror/language': 6.11.3 - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 '@codemirror/lang-css@6.3.1': dependencies: '@codemirror/autocomplete': 6.20.0 '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/css': 1.3.0 '@codemirror/lang-html@6.4.11': @@ -14722,8 +15262,8 @@ snapshots: '@codemirror/lang-javascript': 6.2.4 '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 - '@lezer/common': 1.3.0 + '@codemirror/view': 6.39.4 + '@lezer/common': 1.4.0 '@lezer/css': 1.3.0 '@lezer/html': 1.3.12 @@ -14733,8 +15273,8 @@ snapshots: '@codemirror/language': 6.11.3 '@codemirror/lint': 6.9.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 - '@lezer/common': 1.3.0 + '@codemirror/view': 6.39.4 + '@lezer/common': 1.4.0 '@lezer/javascript': 1.5.4 '@codemirror/lang-sass@6.0.2': @@ -14742,35 +15282,35 @@ snapshots: '@codemirror/lang-css': 6.3.1 '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/sass': 1.1.0 '@codemirror/language@6.11.3': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 - '@lezer/common': 1.3.0 + '@codemirror/view': 6.39.4 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 style-mod: 4.1.3 '@codemirror/lint@6.9.2': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 + '@codemirror/view': 6.39.4 crelt: 1.0.6 '@codemirror/search@6.5.11': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.8 + '@codemirror/view': 6.39.4 crelt: 1.0.6 '@codemirror/state@6.5.2': dependencies: '@marijn/find-cluster-break': 1.0.2 - '@codemirror/view@6.38.8': + '@codemirror/view@6.39.4': dependencies: '@codemirror/state': 6.5.2 crelt: 1.0.6 @@ -14820,6 +15360,10 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + '@csstools/css-syntax-patches-for-csstree@1.0.17': {} '@csstools/css-tokenizer@3.0.4': {} @@ -14895,34 +15439,52 @@ snapshots: '@esbuild/aix-ppc64@0.27.0': optional: true + '@esbuild/aix-ppc64@0.27.1': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm64@0.27.0': optional: true + '@esbuild/android-arm64@0.27.1': + optional: true + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-arm@0.27.0': optional: true + '@esbuild/android-arm@0.27.1': + optional: true + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/android-x64@0.27.0': optional: true + '@esbuild/android-x64@0.27.1': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-arm64@0.27.0': optional: true + '@esbuild/darwin-arm64@0.27.1': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.0': + '@esbuild/darwin-x64@0.27.0': + optional: true + + '@esbuild/darwin-x64@0.27.1': optional: true '@esbuild/freebsd-arm64@0.25.12': @@ -14931,125 +15493,185 @@ snapshots: '@esbuild/freebsd-arm64@0.27.0': optional: true + '@esbuild/freebsd-arm64@0.27.1': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/freebsd-x64@0.27.0': optional: true + '@esbuild/freebsd-x64@0.27.1': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm64@0.27.0': optional: true + '@esbuild/linux-arm64@0.27.1': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.27.0': optional: true + '@esbuild/linux-arm@0.27.1': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.27.0': optional: true + '@esbuild/linux-ia32@0.27.1': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-loong64@0.27.0': optional: true + '@esbuild/linux-loong64@0.27.1': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-mips64el@0.27.0': optional: true + '@esbuild/linux-mips64el@0.27.1': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-ppc64@0.27.0': optional: true + '@esbuild/linux-ppc64@0.27.1': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-riscv64@0.27.0': optional: true + '@esbuild/linux-riscv64@0.27.1': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-s390x@0.27.0': optional: true + '@esbuild/linux-s390x@0.27.1': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true '@esbuild/linux-x64@0.27.0': optional: true + '@esbuild/linux-x64@0.27.1': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true '@esbuild/netbsd-arm64@0.27.0': optional: true + '@esbuild/netbsd-arm64@0.27.1': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true '@esbuild/netbsd-x64@0.27.0': optional: true + '@esbuild/netbsd-x64@0.27.1': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true '@esbuild/openbsd-arm64@0.27.0': optional: true + '@esbuild/openbsd-arm64@0.27.1': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true '@esbuild/openbsd-x64@0.27.0': optional: true + '@esbuild/openbsd-x64@0.27.1': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true '@esbuild/openharmony-arm64@0.27.0': optional: true + '@esbuild/openharmony-arm64@0.27.1': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/sunos-x64@0.27.0': optional: true + '@esbuild/sunos-x64@0.27.1': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-arm64@0.27.0': optional: true + '@esbuild/win32-arm64@0.27.1': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-ia32@0.27.0': optional: true + '@esbuild/win32-ia32@0.27.1': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true '@esbuild/win32-x64@0.27.0': optional: true + '@esbuild/win32-x64@0.27.1': + optional: true + '@externs/nodejs@1.5.0': {} '@fastify/busboy@2.1.1': {} - '@firebase/ai@2.6.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': + '@firebase/ai@2.6.1(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: '@firebase/app': 0.14.6 '@firebase/app-check-interop-types': 0.3.3 @@ -15123,10 +15745,10 @@ snapshots: idb: 7.1.1 tslib: 2.8.1 - '@firebase/auth-compat@0.6.1(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': + '@firebase/auth-compat@0.6.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: '@firebase/app-compat': 0.5.6 - '@firebase/auth': 1.11.1(@firebase/app@0.14.6) + '@firebase/auth': 1.12.0(@firebase/app@0.14.6) '@firebase/auth-types': 0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.13.0) '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 @@ -15143,7 +15765,7 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/auth@1.11.1(@firebase/app@0.14.6)': + '@firebase/auth@1.12.0(@firebase/app@0.14.6)': dependencies: '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 @@ -15189,11 +15811,11 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.8.1 - '@firebase/firestore-compat@0.4.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': + '@firebase/firestore-compat@0.4.3(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/firestore': 4.9.2(@firebase/app@0.14.6) + '@firebase/firestore': 4.9.3(@firebase/app@0.14.6) '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.13.0) '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -15206,7 +15828,7 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/firestore@4.9.2(@firebase/app@0.14.6)': + '@firebase/firestore@4.9.3(@firebase/app@0.14.6)': dependencies: '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 @@ -15459,12 +16081,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@google/genai@1.30.0(@modelcontextprotocol/sdk@1.22.0)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': + '@google/genai@1.33.0(@modelcontextprotocol/sdk@1.24.3)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': dependencies: google-auth-library: 10.5.0(supports-color@10.2.2) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: - '@modelcontextprotocol/sdk': 1.22.0 + '@modelcontextprotocol/sdk': 1.24.3 transitivePeerDependencies: - bufferutil - supports-color @@ -15484,7 +16106,7 @@ snapshots: '@grpc/grpc-js@1.9.15': dependencies: '@grpc/proto-loader': 0.7.15 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@grpc/proto-loader@0.7.15': dependencies: @@ -15527,6 +16149,8 @@ snapshots: '@inquirer/ansi@2.0.1': {} + '@inquirer/ansi@2.0.2': {} + '@inquirer/checkbox@4.3.2(@types/node@20.19.25)': dependencies: '@inquirer/ansi': 1.0.2 @@ -15537,15 +16161,15 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/checkbox@4.3.2(@types/node@24.10.1)': + '@inquirer/checkbox@4.3.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/checkbox@5.0.1(@types/node@20.19.25)': dependencies: @@ -15556,14 +16180,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/checkbox@5.0.1(@types/node@24.10.1)': + '@inquirer/checkbox@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/ansi': 2.0.1 - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/figures': 2.0.1 - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/confirm@5.1.21(@types/node@20.19.25)': dependencies: @@ -15572,12 +16196,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/confirm@5.1.21(@types/node@24.10.1)': + '@inquirer/confirm@5.1.21(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/confirm@6.0.1(@types/node@20.19.25)': dependencies: @@ -15586,12 +16210,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/confirm@6.0.1(@types/node@24.10.1)': + '@inquirer/confirm@6.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/core@10.3.2(@types/node@20.19.25)': dependencies: @@ -15606,24 +16230,24 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/core@10.3.2(@types/node@24.10.1)': + '@inquirer/core@10.3.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.4) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/core@11.0.1(@types/node@20.19.25)': dependencies: '@inquirer/ansi': 2.0.1 '@inquirer/figures': 2.0.1 - '@inquirer/type': 4.0.1(@types/node@20.19.25) + '@inquirer/type': 4.0.2(@types/node@20.19.25) cli-width: 4.1.0 mute-stream: 3.0.0 signal-exit: 4.1.0 @@ -15631,17 +16255,17 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/core@11.0.1(@types/node@24.10.1)': + '@inquirer/core@11.1.0(@types/node@24.10.4)': dependencies: - '@inquirer/ansi': 2.0.1 - '@inquirer/figures': 2.0.1 - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/ansi': 2.0.2 + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) cli-width: 4.1.0 mute-stream: 3.0.0 signal-exit: 4.1.0 wrap-ansi: 9.0.2 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/editor@4.2.23(@types/node@20.19.25)': dependencies: @@ -15651,13 +16275,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/editor@4.2.23(@types/node@24.10.1)': + '@inquirer/editor@4.2.23(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/editor@5.0.1(@types/node@20.19.25)': dependencies: @@ -15667,13 +16291,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/editor@5.0.1(@types/node@24.10.1)': + '@inquirer/editor@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/external-editor': 2.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/external-editor': 2.0.2(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/expand@4.0.23(@types/node@20.19.25)': dependencies: @@ -15683,13 +16307,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/expand@4.0.23(@types/node@24.10.1)': + '@inquirer/expand@4.0.23(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/expand@5.0.1(@types/node@20.19.25)': dependencies: @@ -15698,12 +16322,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/expand@5.0.1(@types/node@24.10.1)': + '@inquirer/expand@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/external-editor@1.0.3(@types/node@20.19.25)': dependencies: @@ -15712,12 +16336,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + '@inquirer/external-editor@1.0.3(@types/node@24.10.4)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/external-editor@2.0.1(@types/node@20.19.25)': dependencies: @@ -15726,17 +16350,19 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/external-editor@2.0.1(@types/node@24.10.1)': + '@inquirer/external-editor@2.0.2(@types/node@24.10.4)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.1': {} + '@inquirer/figures@2.0.2': {} + '@inquirer/input@4.3.1(@types/node@20.19.25)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.25) @@ -15744,12 +16370,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/input@4.3.1(@types/node@24.10.1)': + '@inquirer/input@4.3.1(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/input@5.0.1(@types/node@20.19.25)': dependencies: @@ -15758,12 +16384,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/input@5.0.1(@types/node@24.10.1)': + '@inquirer/input@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/number@3.0.23(@types/node@20.19.25)': dependencies: @@ -15772,12 +16398,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/number@3.0.23(@types/node@24.10.1)': + '@inquirer/number@3.0.23(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/number@4.0.1(@types/node@20.19.25)': dependencies: @@ -15786,12 +16412,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/number@4.0.1(@types/node@24.10.1)': + '@inquirer/number@4.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/password@4.0.23(@types/node@20.19.25)': dependencies: @@ -15801,13 +16427,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/password@4.0.23(@types/node@24.10.1)': + '@inquirer/password@4.0.23(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/password@5.0.1(@types/node@20.19.25)': dependencies: @@ -15817,13 +16443,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/password@5.0.1(@types/node@24.10.1)': + '@inquirer/password@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/ansi': 2.0.1 - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/prompts@7.10.1(@types/node@20.19.25)': dependencies: @@ -15840,20 +16466,20 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/prompts@7.10.1(@types/node@24.10.1)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.10.1) - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@inquirer/editor': 4.2.23(@types/node@24.10.1) - '@inquirer/expand': 4.0.23(@types/node@24.10.1) - '@inquirer/input': 4.3.1(@types/node@24.10.1) - '@inquirer/number': 3.0.23(@types/node@24.10.1) - '@inquirer/password': 4.0.23(@types/node@24.10.1) - '@inquirer/rawlist': 4.1.11(@types/node@24.10.1) - '@inquirer/search': 3.2.2(@types/node@24.10.1) - '@inquirer/select': 4.4.2(@types/node@24.10.1) + '@inquirer/prompts@7.10.1(@types/node@24.10.4)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.10.4) + '@inquirer/confirm': 5.1.21(@types/node@24.10.4) + '@inquirer/editor': 4.2.23(@types/node@24.10.4) + '@inquirer/expand': 4.0.23(@types/node@24.10.4) + '@inquirer/input': 4.3.1(@types/node@24.10.4) + '@inquirer/number': 3.0.23(@types/node@24.10.4) + '@inquirer/password': 4.0.23(@types/node@24.10.4) + '@inquirer/rawlist': 4.1.11(@types/node@24.10.4) + '@inquirer/search': 3.2.2(@types/node@24.10.4) + '@inquirer/select': 4.4.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/prompts@8.0.1(@types/node@20.19.25)': dependencies: @@ -15870,20 +16496,20 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/prompts@8.0.1(@types/node@24.10.1)': - dependencies: - '@inquirer/checkbox': 5.0.1(@types/node@24.10.1) - '@inquirer/confirm': 6.0.1(@types/node@24.10.1) - '@inquirer/editor': 5.0.1(@types/node@24.10.1) - '@inquirer/expand': 5.0.1(@types/node@24.10.1) - '@inquirer/input': 5.0.1(@types/node@24.10.1) - '@inquirer/number': 4.0.1(@types/node@24.10.1) - '@inquirer/password': 5.0.1(@types/node@24.10.1) - '@inquirer/rawlist': 5.0.1(@types/node@24.10.1) - '@inquirer/search': 4.0.1(@types/node@24.10.1) - '@inquirer/select': 5.0.1(@types/node@24.10.1) + '@inquirer/prompts@8.1.0(@types/node@24.10.4)': + dependencies: + '@inquirer/checkbox': 5.0.3(@types/node@24.10.4) + '@inquirer/confirm': 6.0.3(@types/node@24.10.4) + '@inquirer/editor': 5.0.3(@types/node@24.10.4) + '@inquirer/expand': 5.0.3(@types/node@24.10.4) + '@inquirer/input': 5.0.3(@types/node@24.10.4) + '@inquirer/number': 4.0.3(@types/node@24.10.4) + '@inquirer/password': 5.0.3(@types/node@24.10.4) + '@inquirer/rawlist': 5.1.0(@types/node@24.10.4) + '@inquirer/search': 4.0.3(@types/node@24.10.4) + '@inquirer/select': 5.0.3(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/rawlist@4.1.11(@types/node@20.19.25)': dependencies: @@ -15893,13 +16519,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/rawlist@4.1.11(@types/node@24.10.1)': + '@inquirer/rawlist@4.1.11(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/rawlist@5.0.1(@types/node@20.19.25)': dependencies: @@ -15908,12 +16534,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/rawlist@5.0.1(@types/node@24.10.1)': + '@inquirer/rawlist@5.1.0(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/search@3.2.2(@types/node@20.19.25)': dependencies: @@ -15924,14 +16550,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/search@3.2.2(@types/node@24.10.1)': + '@inquirer/search@3.2.2(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/search@4.0.1(@types/node@20.19.25)': dependencies: @@ -15941,13 +16567,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/search@4.0.1(@types/node@24.10.1)': + '@inquirer/search@4.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/figures': 2.0.1 - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/select@4.4.2(@types/node@20.19.25)': dependencies: @@ -15959,15 +16585,15 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/select@4.4.2(@types/node@24.10.1)': + '@inquirer/select@4.4.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/select@5.0.1(@types/node@20.19.25)': dependencies: @@ -15978,30 +16604,34 @@ snapshots: optionalDependencies: '@types/node': 20.19.25 - '@inquirer/select@5.0.1(@types/node@24.10.1)': + '@inquirer/select@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/ansi': 2.0.1 - '@inquirer/core': 11.0.1(@types/node@24.10.1) - '@inquirer/figures': 2.0.1 - '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/type@3.0.10(@types/node@20.19.25)': optionalDependencies: '@types/node': 20.19.25 - '@inquirer/type@3.0.10(@types/node@24.10.1)': + '@inquirer/type@3.0.10(@types/node@24.10.4)': optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@inquirer/type@4.0.1(@types/node@20.19.25)': optionalDependencies: '@types/node': 20.19.25 - '@inquirer/type@4.0.1(@types/node@24.10.1)': + '@inquirer/type@4.0.2(@types/node@20.19.25)': optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 20.19.25 + + '@inquirer/type@4.0.2(@types/node@24.10.4)': + optionalDependencies: + '@types/node': 24.10.4 '@isaacs/balanced-match@4.0.1': {} @@ -16035,7 +16665,7 @@ snapshots: '@jest/console@30.2.0': dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 jest-message-util: 30.2.0 jest-util: 30.2.0 @@ -16049,14 +16679,14 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.2.0 - jest-config: 30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@24.10.4)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) jest-haste-map: 30.2.0 jest-message-util: 30.2.0 jest-regex-util: 30.0.1 @@ -16086,14 +16716,14 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.2.0 - jest-config: 30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@24.10.4)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-haste-map: 30.2.0 jest-message-util: 30.2.0 jest-regex-util: 30.0.1 @@ -16122,7 +16752,7 @@ snapshots: '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 '@types/jsdom': 21.1.7 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-mock: 30.2.0 jest-util: 30.2.0 jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) @@ -16131,7 +16761,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-mock: 30.2.0 '@jest/expect-utils@30.2.0': @@ -16149,7 +16779,7 @@ snapshots: dependencies: '@jest/types': 30.2.0 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-message-util: 30.2.0 jest-mock: 30.2.0 jest-util: 30.2.0 @@ -16167,7 +16797,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-regex-util: 30.0.1 '@jest/reporters@30.2.0': @@ -16178,7 +16808,7 @@ snapshots: '@jest/transform': 30.2.0 '@jest/types': 30.2.0 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit-x: 0.2.2 @@ -16255,7 +16885,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -16330,39 +16960,39 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@lezer/common@1.3.0': {} + '@lezer/common@1.4.0': {} '@lezer/css@1.3.0': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 '@lezer/highlight@1.2.3': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/html@1.3.12': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 '@lezer/javascript@1.5.4': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 - '@lezer/lr@1.4.3': + '@lezer/lr@1.4.5': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/sass@1.1.0': dependencies: - '@lezer/common': 1.3.0 + '@lezer/common': 1.4.0 '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.3 + '@lezer/lr': 1.4.5 '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.25))(@types/node@20.19.25)(listr2@9.0.5)': dependencies: @@ -16372,10 +17002,10 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.4))(@types/node@24.10.4)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/prompts': 7.10.1(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -16443,7 +17073,7 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} - '@modelcontextprotocol/sdk@1.22.0': + '@modelcontextprotocol/sdk@1.24.3': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1 @@ -16454,10 +17084,11 @@ snapshots: eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) + jose: 6.1.3 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod: 4.1.13 + zod-to-json-schema: 3.25.0(zod@4.1.13) transitivePeerDependencies: - supports-color @@ -16567,7 +17198,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.0.7': + '@napi-rs/wasm-runtime@1.1.0': dependencies: '@emnapi/core': 1.7.1 '@emnapi/runtime': 1.7.1 @@ -16585,11 +17216,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@ngtools/webpack@21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0))': + '@ngtools/webpack@21.1.0-next.2(@angular/compiler-cli@packages+compiler-cli)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1))': dependencies: '@angular/compiler-cli': link:packages/compiler-cli typescript: 5.9.3 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': optional: true @@ -16622,7 +17253,7 @@ snapshots: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6(supports-color@10.2.2) - lru-cache: 11.2.2 + lru-cache: 11.2.4 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -16640,7 +17271,7 @@ snapshots: dependencies: '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 npm-pick-manifest: 11.0.3 proc-log: 6.0.0 promise-retry: 2.0.1 @@ -16884,7 +17515,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.38.0': {} - '@oxc-project/types@0.98.0': {} + '@oxc-project/types@0.101.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -16962,10 +17593,10 @@ snapshots: '@pnpm/crypto.polyfill@1000.1.0': {} - '@pnpm/dependency-path@1001.1.5': + '@pnpm/dependency-path@1001.1.8': dependencies: '@pnpm/crypto.hash': 1000.2.1 - '@pnpm/types': 1001.0.1 + '@pnpm/types': 1001.2.0 semver: 7.7.3 '@pnpm/graceful-fs@1000.0.1': @@ -16982,7 +17613,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@pnpm/types@1001.0.1': {} + '@pnpm/types@1001.2.0': {} '@protobufjs/aspromise@1.1.2': {} @@ -17022,66 +17653,63 @@ snapshots: - react-native-b4a - supports-color - '@rolldown/binding-android-arm64@1.0.0-beta.51': + '@rolldown/binding-android-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.51': + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.51': + '@rolldown/binding-darwin-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.51': + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.51': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.51': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.51': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51': + '@napi-rs/wasm-runtime': 1.1.0 optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': optional: true - '@rolldown/pluginutils@1.0.0-beta.51': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} - '@rollup/plugin-babel@6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.53.3)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.53.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.53.3 + rollup: 4.53.5 transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@29.0.0(rollup@4.53.3)': + '@rollup/plugin-commonjs@29.0.0(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -17089,7 +17717,7 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.53.5 '@rollup/plugin-json@6.1.0(rollup@4.53.2)': dependencies: @@ -17097,23 +17725,23 @@ snapshots: optionalDependencies: rollup: 4.53.2 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.3)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.53.3 + rollup: 4.53.5 - '@rollup/pluginutils@5.2.0(rollup@4.53.3)': + '@rollup/pluginutils@5.2.0(rollup@4.53.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.53.5 '@rollup/pluginutils@5.3.0(rollup@4.53.2)': dependencies: @@ -17123,144 +17751,144 @@ snapshots: optionalDependencies: rollup: 4.53.2 - '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + '@rollup/pluginutils@5.3.0(rollup@4.53.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.53.5 '@rollup/rollup-android-arm-eabi@4.53.2': optional: true - '@rollup/rollup-android-arm-eabi@4.53.3': + '@rollup/rollup-android-arm-eabi@4.53.5': optional: true '@rollup/rollup-android-arm64@4.53.2': optional: true - '@rollup/rollup-android-arm64@4.53.3': + '@rollup/rollup-android-arm64@4.53.5': optional: true '@rollup/rollup-darwin-arm64@4.53.2': optional: true - '@rollup/rollup-darwin-arm64@4.53.3': + '@rollup/rollup-darwin-arm64@4.53.5': optional: true '@rollup/rollup-darwin-x64@4.53.2': optional: true - '@rollup/rollup-darwin-x64@4.53.3': + '@rollup/rollup-darwin-x64@4.53.5': optional: true '@rollup/rollup-freebsd-arm64@4.53.2': optional: true - '@rollup/rollup-freebsd-arm64@4.53.3': + '@rollup/rollup-freebsd-arm64@4.53.5': optional: true '@rollup/rollup-freebsd-x64@4.53.2': optional: true - '@rollup/rollup-freebsd-x64@4.53.3': + '@rollup/rollup-freebsd-x64@4.53.5': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.53.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': optional: true '@rollup/rollup-linux-arm-musleabihf@4.53.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.3': + '@rollup/rollup-linux-arm-musleabihf@4.53.5': optional: true '@rollup/rollup-linux-arm64-gnu@4.53.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.3': + '@rollup/rollup-linux-arm64-gnu@4.53.5': optional: true '@rollup/rollup-linux-arm64-musl@4.53.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.3': + '@rollup/rollup-linux-arm64-musl@4.53.5': optional: true '@rollup/rollup-linux-loong64-gnu@4.53.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.3': + '@rollup/rollup-linux-loong64-gnu@4.53.5': optional: true '@rollup/rollup-linux-ppc64-gnu@4.53.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.3': + '@rollup/rollup-linux-ppc64-gnu@4.53.5': optional: true '@rollup/rollup-linux-riscv64-gnu@4.53.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.3': + '@rollup/rollup-linux-riscv64-gnu@4.53.5': optional: true '@rollup/rollup-linux-riscv64-musl@4.53.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.3': + '@rollup/rollup-linux-riscv64-musl@4.53.5': optional: true '@rollup/rollup-linux-s390x-gnu@4.53.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.3': + '@rollup/rollup-linux-s390x-gnu@4.53.5': optional: true '@rollup/rollup-linux-x64-gnu@4.53.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.3': + '@rollup/rollup-linux-x64-gnu@4.53.5': optional: true '@rollup/rollup-linux-x64-musl@4.53.2': optional: true - '@rollup/rollup-linux-x64-musl@4.53.3': + '@rollup/rollup-linux-x64-musl@4.53.5': optional: true '@rollup/rollup-openharmony-arm64@4.53.2': optional: true - '@rollup/rollup-openharmony-arm64@4.53.3': + '@rollup/rollup-openharmony-arm64@4.53.5': optional: true '@rollup/rollup-win32-arm64-msvc@4.53.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.3': + '@rollup/rollup-win32-arm64-msvc@4.53.5': optional: true '@rollup/rollup-win32-ia32-msvc@4.53.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.3': + '@rollup/rollup-win32-ia32-msvc@4.53.5': optional: true '@rollup/rollup-win32-x64-gnu@4.53.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.3': + '@rollup/rollup-win32-x64-gnu@4.53.5': optional: true '@rollup/rollup-win32-x64-msvc@4.53.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.3': + '@rollup/rollup-win32-x64-msvc@4.53.5': optional: true '@rollup/wasm-node@4.53.3': @@ -17308,10 +17936,10 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@schematics/angular@21.1.0-next.0(chokidar@4.0.3)': + '@schematics/angular@21.1.0-next.2(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.1.0-next.0(chokidar@4.0.3) - '@angular-devkit/schematics': 21.1.0-next.0(chokidar@4.0.3) + '@angular-devkit/core': 21.1.0-next.2(chokidar@5.0.0) + '@angular-devkit/schematics': 21.1.0-next.2(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -17397,30 +18025,61 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 + '@shikijs/core@3.20.0': + dependencies: + '@shikijs/types': 3.20.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + '@shikijs/engine-javascript@3.15.0': dependencies: '@shikijs/types': 3.15.0 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.4 + '@shikijs/engine-javascript@3.20.0': + dependencies: + '@shikijs/types': 3.20.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + '@shikijs/engine-oniguruma@3.15.0': dependencies: '@shikijs/types': 3.15.0 '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@3.20.0': + dependencies: + '@shikijs/types': 3.20.0 + '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/langs@3.15.0': dependencies: '@shikijs/types': 3.15.0 + '@shikijs/langs@3.20.0': + dependencies: + '@shikijs/types': 3.20.0 + '@shikijs/themes@3.15.0': dependencies: '@shikijs/types': 3.15.0 + '@shikijs/themes@3.20.0': + dependencies: + '@shikijs/types': 3.20.0 + '@shikijs/types@3.15.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + '@shikijs/types@3.20.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/vscode-textmate@10.0.2': {} '@sigstore/bundle@4.0.0': @@ -17548,7 +18207,7 @@ snapshots: '@types/adm-zip@0.5.7': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/angular@1.8.9': {} @@ -17580,17 +18239,17 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/bonjour@3.5.13': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -17609,22 +18268,22 @@ snapshots: '@types/cli-progress@3.11.6': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.7 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/connect@3.4.38': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/convert-source-map@2.0.3': {} '@types/cors@2.8.19': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/d3-array@3.2.2': {} @@ -17749,7 +18408,7 @@ snapshots: '@types/duplexify@3.6.5': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/ejs@3.1.5': {} @@ -17769,7 +18428,7 @@ snapshots: '@types/express-serve-static-core@4.19.7': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -17795,7 +18454,7 @@ snapshots: '@types/git-raw-commits@5.0.1': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/hammerjs@2.0.46': {} @@ -17811,7 +18470,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/istanbul-lib-coverage@2.0.6': {} @@ -17835,13 +18494,13 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 '@types/jsdom@27.0.0': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -17849,7 +18508,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/mdast@4.0.4': dependencies: @@ -17859,7 +18518,7 @@ snapshots: '@types/node-forge@1.3.14': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/node@20.19.25': dependencies: @@ -17873,12 +18532,16 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/node@24.10.4': + dependencies: + undici-types: 7.16.0 + '@types/normalize-package-data@2.4.4': {} '@types/pumpify@1.4.5': dependencies: '@types/duplexify': 3.6.5 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/puppeteer-core@5.4.0(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@6.0.5)': dependencies: @@ -17914,7 +18577,7 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/retry@0.12.2': {} @@ -17924,7 +18587,7 @@ snapshots: '@types/selenium-webdriver@4.35.4': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/ws': 8.18.1 '@types/semver@7.7.1': {} @@ -17932,11 +18595,11 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/send@1.2.1': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/serve-index@1.9.4': dependencies: @@ -17945,7 +18608,7 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/send': 0.17.6 '@types/sinonjs__fake-timers@8.1.1': {} @@ -17954,7 +18617,7 @@ snapshots: '@types/sockjs@0.3.36': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/stack-trace@0.0.33': {} @@ -17981,7 +18644,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@types/yargs-parser@21.0.3': {} @@ -17993,7 +18656,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 optional: true '@typescript/vfs@1.6.2(typescript@5.9.3)': @@ -18072,13 +18735,13 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': dependencies: - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/expect@4.0.13': dependencies: @@ -18089,22 +18752,30 @@ snapshots: chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.13(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.0.13(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.13 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) optional: true - '@vitest/mocker@4.0.13(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.0.13(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.13 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/mocker@4.0.13(vite@7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.13 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.13': dependencies: @@ -18426,22 +19097,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.45.0: - dependencies: - '@algolia/abtesting': 1.11.0 - '@algolia/client-abtesting': 5.45.0 - '@algolia/client-analytics': 5.45.0 - '@algolia/client-common': 5.45.0 - '@algolia/client-insights': 5.45.0 - '@algolia/client-personalization': 5.45.0 - '@algolia/client-query-suggestions': 5.45.0 - '@algolia/client-search': 5.45.0 - '@algolia/ingestion': 1.45.0 - '@algolia/monitoring': 1.45.0 - '@algolia/recommend': 5.45.0 - '@algolia/requester-browser-xhr': 5.45.0 - '@algolia/requester-fetch': 5.45.0 - '@algolia/requester-node-http': 5.45.0 + algoliasearch@5.46.0: + dependencies: + '@algolia/abtesting': 1.12.0 + '@algolia/client-abtesting': 5.46.0 + '@algolia/client-analytics': 5.46.0 + '@algolia/client-common': 5.46.0 + '@algolia/client-insights': 5.46.0 + '@algolia/client-personalization': 5.46.0 + '@algolia/client-query-suggestions': 5.46.0 + '@algolia/client-search': 5.46.0 + '@algolia/ingestion': 1.46.0 + '@algolia/monitoring': 1.46.0 + '@algolia/recommend': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 angular-mocks@1.5.11: {} @@ -18675,6 +19346,15 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 + autoprefixer@10.4.23(postcss@8.5.6): + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001760 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -18703,11 +19383,11 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.0)): + babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.27.1)): dependencies: '@babel/core': 7.28.5 find-up: 5.0.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) babel-plugin-istanbul@7.0.1: dependencies: @@ -18823,6 +19503,8 @@ snapshots: baseline-browser-mapping@2.8.31: {} + baseline-browser-mapping@2.9.7: {} + basic-auth-connect@1.1.0: dependencies: tsscmp: 1.0.6 @@ -18973,6 +19655,14 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.7 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) + browserstack@1.6.1: dependencies: https-proxy-agent: 7.0.6(supports-color@10.2.2) @@ -19043,7 +19733,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.0 - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 minipass-collect: 2.0.1 minipass-flush: 1.0.5 @@ -19100,6 +19790,8 @@ snapshots: caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001760: {} + capital-case@1.0.4: dependencies: no-case: 3.0.4 @@ -19221,15 +19913,17 @@ snapshots: dependencies: readdirp: 4.1.2 - chownr@1.1.4: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 - chownr@2.0.0: {} + chownr@1.1.4: {} chownr@3.0.0: {} chrome-launcher@0.13.4: dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 escape-string-regexp: 1.0.5 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -19256,7 +19950,7 @@ snapshots: dependencies: json-parse-helpfulerror: 1.0.3 - cldr@7.9.0: + cldr@8.0.0: dependencies: '@xmldom/xmldom': 0.8.11 escodegen: 2.1.0 @@ -19639,14 +20333,14 @@ snapshots: each-props: 3.0.0 is-plain-object: 5.0.0 - copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.0)): + copy-webpack-plugin@13.0.1(webpack@5.103.0(esbuild@0.27.1)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 tinyglobby: 0.2.15 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) core-js-compat@3.47.0: dependencies: @@ -19710,7 +20404,7 @@ snapshots: crypto-random-string@2.0.0: {} - css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.0)): + css-loader@7.1.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -19721,7 +20415,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) css-select@5.2.2: dependencies: @@ -19765,11 +20459,19 @@ snapshots: '@csstools/css-syntax-patches-for-csstree': 1.0.17 css-tree: 3.1.0 + cssstyle@5.3.4(postcss@8.5.6): + dependencies: + '@asamuzakjp/css-color': 4.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6) + css-tree: 3.1.0 + transitivePeerDependencies: + - postcss + csv-parse@5.6.0: {} custom-event@1.0.1: {} - cypress@15.7.0: + cypress@15.7.1: dependencies: '@cypress/request': 3.0.9 '@cypress/xvfb': 1.2.4(supports-color@8.1.1) @@ -20313,6 +21015,8 @@ snapshots: electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.267: {} + emittery@0.13.1: {} emoji-regex@10.6.0: {} @@ -20349,7 +21053,7 @@ snapshots: engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@types/cors': 2.8.19 - '@types/node': 24.10.1 + '@types/node': 24.10.4 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -20486,7 +21190,7 @@ snapshots: esbuild-plugin-umd-wrapper@3.0.0: {} - esbuild-wasm@0.27.0: {} + esbuild-wasm@0.27.1: {} esbuild@0.25.12: optionalDependencies: @@ -20546,6 +21250,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.0 '@esbuild/win32-x64': 0.27.0 + esbuild@0.27.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 + escalade@3.2.0: {} escape-goat@2.1.1: {} @@ -20946,7 +21679,7 @@ snapshots: object.pick: 1.3.0 parse-filepath: 1.0.2 - firebase-tools@14.26.0(@types/node@20.19.25)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.5): + firebase-tools@15.0.0(@types/node@20.19.25)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.5): dependencies: '@apphosting/build': 0.1.7(@types/node@20.19.25)(typescript@5.9.3) '@apphosting/common': 0.0.8 @@ -20955,7 +21688,7 @@ snapshots: '@google-cloud/cloud-sql-connector': 1.8.4 '@google-cloud/pubsub': 5.2.0 '@inquirer/prompts': 7.10.1(@types/node@20.19.25) - '@modelcontextprotocol/sdk': 1.22.0 + '@modelcontextprotocol/sdk': 1.24.3 abort-controller: 3.0.0 ajv: 8.17.1 ajv-formats: 3.0.1 @@ -21011,7 +21744,6 @@ snapshots: stream-chain: 2.2.5 stream-json: 1.9.1 superstatic: 10.0.0(encoding@0.1.13) - tar: 6.2.1 tcp-port-used: 1.0.2 tmp: 0.2.5 triple-beam: 1.4.1 @@ -21021,7 +21753,7 @@ snapshots: winston: 3.18.3 winston-transport: 4.9.0 ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@6.0.5) - yaml: 2.8.1 + yaml: 2.8.2 zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: @@ -21038,9 +21770,9 @@ snapshots: - typescript - utf-8-validate - firebase@12.6.0: + firebase@12.7.0: dependencies: - '@firebase/ai': 2.6.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/ai': 2.6.1(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) '@firebase/analytics': 0.10.19(@firebase/app@0.14.6) '@firebase/analytics-compat': 0.2.25(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) '@firebase/app': 0.14.6 @@ -21048,13 +21780,13 @@ snapshots: '@firebase/app-check-compat': 0.4.0(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) '@firebase/app-compat': 0.5.6 '@firebase/app-types': 0.9.3 - '@firebase/auth': 1.11.1(@firebase/app@0.14.6) - '@firebase/auth-compat': 0.6.1(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/auth': 1.12.0(@firebase/app@0.14.6) + '@firebase/auth-compat': 0.6.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) '@firebase/data-connect': 0.3.12(@firebase/app@0.14.6) '@firebase/database': 1.1.0 '@firebase/database-compat': 2.1.0 - '@firebase/firestore': 4.9.2(@firebase/app@0.14.6) - '@firebase/firestore-compat': 0.4.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/firestore': 4.9.3(@firebase/app@0.14.6) + '@firebase/firestore-compat': 0.4.3(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) '@firebase/functions': 0.13.1(@firebase/app@0.14.6) '@firebase/functions-compat': 0.4.1(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) '@firebase/installations': 0.6.19(@firebase/app@0.14.6) @@ -21162,10 +21894,6 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - fs-minipass@3.0.3: dependencies: minipass: 7.1.2 @@ -21702,7 +22430,7 @@ snapshots: hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 hpack.js@2.1.6: dependencies: @@ -22303,7 +23031,7 @@ snapshots: jasmine-core@4.6.1: {} - jasmine-core@5.12.1: {} + jasmine-core@5.13.0: {} jasmine-reporters@2.5.2: dependencies: @@ -22316,10 +23044,10 @@ snapshots: glob: 7.2.3 jasmine-core: 2.8.0 - jasmine@5.12.0: + jasmine@5.13.0: dependencies: glob: 10.5.0 - jasmine-core: 5.12.1 + jasmine-core: 5.13.0 jasminewd2@2.2.0: {} @@ -22335,7 +23063,7 @@ snapshots: '@jest/expect': 30.2.0 '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.0 @@ -22428,7 +23156,7 @@ snapshots: - supports-color optional: true - jest-config@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 @@ -22456,13 +23184,46 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.10.1 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@30.2.0(@types/node@24.10.4)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.28.5 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.28.5) + chalk: 4.1.2 + ci-info: 4.3.1 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 24.10.4 ts-node: 10.9.2(@types/node@20.19.25)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color optional: true - jest-config@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@24.10.4)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 @@ -22489,7 +23250,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros @@ -22519,7 +23280,7 @@ snapshots: '@jest/environment': 30.2.0 '@jest/environment-jsdom-abstract': 30.2.0(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)) '@types/jsdom': 21.1.7 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil @@ -22531,7 +23292,7 @@ snapshots: '@jest/environment': 30.2.0 '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-mock: 30.2.0 jest-util: 30.2.0 jest-validate: 30.2.0 @@ -22539,7 +23300,7 @@ snapshots: jest-haste-map@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -22578,7 +23339,7 @@ snapshots: jest-mock@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 jest-util: 30.2.0 jest-pnp-resolver@1.2.3(jest-resolve@30.2.0): @@ -22612,7 +23373,7 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -22641,7 +23402,7 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 cjs-module-lexer: 2.1.1 collect-v8-coverage: 1.0.3 @@ -22688,7 +23449,7 @@ snapshots: jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 chalk: 4.1.2 ci-info: 4.3.1 graceful-fs: 4.2.11 @@ -22707,7 +23468,7 @@ snapshots: dependencies: '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -22716,13 +23477,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@30.2.0: dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@ungap/structured-clone': 1.3.0 jest-util: 30.2.0 merge-stream: 2.0.0 @@ -22767,6 +23528,8 @@ snapshots: url-join: 0.0.1 valid-url: 1.0.9 + jose@6.1.3: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -22782,24 +23545,51 @@ snapshots: jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.22 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + '@acemir/cssom': 0.9.24 + '@asamuzakjp/dom-selector': 6.7.4 + cssstyle: 5.3.3 + data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 - parse5: 7.3.0 - rrweb-cssom: 0.8.0 + parse5: 8.0.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 5.1.2 + tough-cookie: 6.0.0 w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 + webidl-conversions: 8.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 + whatwg-url: 15.1.0 ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) xml-name-validator: 5.0.0 transitivePeerDependencies: @@ -22807,11 +23597,11 @@ snapshots: - supports-color - utf-8-validate - jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): + jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5): dependencies: - '@acemir/cssom': 0.9.24 - '@asamuzakjp/dom-selector': 6.7.4 - cssstyle: 5.3.3 + '@acemir/cssom': 0.9.28 + '@asamuzakjp/dom-selector': 6.7.6 + cssstyle: 5.3.4(postcss@8.5.6) data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 @@ -22831,6 +23621,7 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil + - postcss - supports-color - utf-8-validate @@ -22952,9 +23743,9 @@ snapshots: is-wsl: 2.2.0 which: 3.0.1 - karma-jasmine-html-reporter@2.1.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)): + karma-jasmine-html-reporter@2.1.0(jasmine-core@5.13.0)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)): dependencies: - jasmine-core: 5.12.1 + jasmine-core: 5.13.0 karma: 6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) karma-jasmine: 5.1.0(karma@6.4.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)) @@ -23073,11 +23864,11 @@ snapshots: lead@4.0.0: {} - less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.0)): + less-loader@12.3.0(less@4.4.2)(webpack@5.103.0(esbuild@0.27.1)): dependencies: less: 4.4.2 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) less@4.4.2: dependencies: @@ -23101,11 +23892,11 @@ snapshots: libsodium@0.7.15: {} - license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.0)): + license-webpack-plugin@4.0.2(webpack@5.103.0(esbuild@0.27.1)): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) lie@3.3.0: dependencies: @@ -23314,6 +24105,8 @@ snapshots: lru-cache@11.2.2: {} + lru-cache@11.2.4: {} + lru-cache@2.5.0: {} lru-cache@5.1.1: @@ -23498,6 +24291,31 @@ snapshots: transitivePeerDependencies: - supports-color + mermaid@11.12.2: + dependencies: + '@braintree/sanitize-url': 7.1.1 + '@iconify/utils': 3.0.2 + '@mermaid-js/parser': 0.6.3 + '@types/d3': 7.4.3 + cytoscape: 3.33.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) + cytoscape-fcose: 2.2.0(cytoscape@3.33.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.13(patch_hash=08ae1f30d46402ef53e1486454faa875683cf4152a8aab71ac255078ebfaccdd) + dayjs: 1.11.19 + dompurify: 3.3.0 + katex: 0.16.25 + khroma: 2.1.0 + lodash-es: 4.17.21 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 11.1.0 + transitivePeerDependencies: + - supports-color + methods@1.1.2: {} micromark-util-character@2.1.1: @@ -23546,11 +24364,11 @@ snapshots: mimic-response@3.1.0: {} - mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.0)): + mini-css-extract-plugin@2.9.4(webpack@5.103.0(esbuild@0.27.1)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) minimalistic-assert@1.0.1: {} @@ -23621,15 +24439,8 @@ snapshots: dependencies: yallist: 4.0.0 - minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - minizlib@3.1.0: dependencies: minipass: 7.1.2 @@ -23777,10 +24588,40 @@ snapshots: netmask@2.0.2: {} - ng-packagr@21.1.0-next.0(@angular/compiler-cli@21.1.0-next.0(@angular/compiler@21.1.0-next.0)(typescript@5.9.3))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3): + ng-packagr@21.1.0-next.0(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3): + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular/compiler-cli': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) + '@rollup/plugin-json': 6.1.0(rollup@4.53.2) + '@rollup/wasm-node': 4.53.3 + ajv: 8.17.1 + ansi-colors: 4.1.3 + browserslist: 4.28.0 + chokidar: 4.0.3 + commander: 14.0.2 + dependency-graph: 1.0.0 + esbuild: 0.27.0 + find-cache-directory: 6.0.0 + injection-js: 2.6.1 + jsonc-parser: 3.3.1 + less: 4.4.2 + ora: 9.0.0 + piscina: 5.1.4 + postcss: 8.5.6 + rollup-plugin-dts: 6.2.3(rollup@4.53.2)(typescript@5.9.3) + rxjs: 7.8.2 + sass: 1.94.2 + tinyglobby: 0.2.15 + tslib: 2.8.1 + typescript: 5.9.3 + optionalDependencies: + rollup: 4.53.2 + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) + + ng-packagr@21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.20.6)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3): dependencies: '@ampproject/remapping': 2.3.0 - '@angular/compiler-cli': 21.1.0-next.0(@angular/compiler@21.1.0-next.0)(typescript@5.9.3) + '@angular/compiler-cli': link:packages/compiler-cli '@rollup/plugin-json': 6.1.0(rollup@4.53.2) '@rollup/wasm-node': 4.53.3 ajv: 8.17.1 @@ -23805,9 +24646,10 @@ snapshots: typescript: 5.9.3 optionalDependencies: rollup: 4.53.2 - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) + tailwindcss: 3.4.19(tsx@4.20.6)(yaml@2.8.2) + optional: true - ng-packagr@21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(tslib@2.8.1)(typescript@5.9.3): + ng-packagr@21.1.0-next.0(@angular/compiler-cli@packages+compiler-cli)(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(tslib@2.8.1)(typescript@5.9.3): dependencies: '@ampproject/remapping': 2.3.0 '@angular/compiler-cli': link:packages/compiler-cli @@ -23835,7 +24677,7 @@ snapshots: typescript: 5.9.3 optionalDependencies: rollup: 4.53.2 - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) optional: true ngx-flamegraph@0.1.1(@angular/common@packages+common)(@angular/core@packages+core): @@ -23844,9 +24686,9 @@ snapshots: '@angular/core': link:packages/core tslib: 2.8.1 - ngx-progressbar@14.0.0(@angular/cdk@21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2): + ngx-progressbar@14.0.0(@angular/cdk@21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2))(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2): dependencies: - '@angular/cdk': 21.1.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(rxjs@7.8.2) + '@angular/cdk': 21.1.0-next.3(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2) '@angular/common': link:packages/common '@angular/core': link:packages/core rxjs: 7.8.2 @@ -24144,7 +24986,7 @@ snapshots: openapi3-ts@3.2.0: dependencies: - yaml: 2.8.1 + yaml: 2.8.2 opener@1.5.2: {} @@ -24504,7 +25346,7 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - playwright-core@1.56.1: {} + playwright-core@1.57.0: {} plugin-error@2.0.1: dependencies: @@ -24542,23 +25384,33 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 tsx: 4.20.6 - yaml: 2.8.1 + yaml: 2.8.2 + optional: true - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.0)): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.6 + tsx: 4.21.0 + yaml: 2.8.2 + + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.27.1)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 semver: 7.7.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - typescript @@ -24620,11 +25472,11 @@ snapshots: powershell-utils@0.1.0: {} - preact-render-to-string@6.6.3(preact@10.27.2): + preact-render-to-string@6.6.4(preact@10.28.0): dependencies: - preact: 10.27.2 + preact: 10.28.0 - preact@10.27.2: {} + preact@10.28.0: {} prebuild-install@7.1.3: dependencies: @@ -24644,6 +25496,8 @@ snapshots: prettier@3.6.2: {} + prettier@3.7.4: {} + pretty-bytes@5.6.0: {} pretty-format@30.2.0: @@ -24693,7 +25547,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.10.1 + '@types/node': 24.10.4 long: 5.3.2 protractor@7.0.0: @@ -24964,6 +25818,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + rechoir@0.8.0: dependencies: resolve: 1.22.11 @@ -25167,25 +26023,24 @@ snapshots: robust-predicates@3.0.2: {} - rolldown@1.0.0-beta.51: + rolldown@1.0.0-beta.53: dependencies: - '@oxc-project/types': 0.98.0 - '@rolldown/pluginutils': 1.0.0-beta.51 + '@oxc-project/types': 0.101.0 + '@rolldown/pluginutils': 1.0.0-beta.53 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.51 - '@rolldown/binding-darwin-x64': 1.0.0-beta.51 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.51 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.51 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.51 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.51 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.51 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.51 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.51 + '@rolldown/binding-android-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-x64': 1.0.0-beta.53 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.53 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.53 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.53 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.53 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53 rollup-plugin-dts@6.2.3(rollup@4.53.2)(typescript@5.9.3): dependencies: @@ -25195,10 +26050,10 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.27.1 - rollup-plugin-dts@6.2.3(rollup@4.53.3)(typescript@5.9.3): + rollup-plugin-dts@6.2.3(rollup@4.53.5)(typescript@5.9.3): dependencies: magic-string: 0.30.21 - rollup: 4.53.3 + rollup: 4.53.5 typescript: 5.9.3 optionalDependencies: '@babel/code-frame': 7.27.1 @@ -25207,10 +26062,10 @@ snapshots: dependencies: magic-string: 0.25.9 - rollup-plugin-sourcemaps2@0.5.4(@types/node@20.19.25)(rollup@4.53.3): + rollup-plugin-sourcemaps2@0.5.4(@types/node@20.19.25)(rollup@4.53.5): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.53.3) - rollup: 4.53.3 + '@rollup/pluginutils': 5.2.0(rollup@4.53.5) + rollup: 4.53.5 optionalDependencies: '@types/node': 20.19.25 @@ -25242,32 +26097,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.2 fsevents: 2.3.3 - rollup@4.53.3: + rollup@4.53.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 + '@rollup/rollup-android-arm-eabi': 4.53.5 + '@rollup/rollup-android-arm64': 4.53.5 + '@rollup/rollup-darwin-arm64': 4.53.5 + '@rollup/rollup-darwin-x64': 4.53.5 + '@rollup/rollup-freebsd-arm64': 4.53.5 + '@rollup/rollup-freebsd-x64': 4.53.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.5 + '@rollup/rollup-linux-arm-musleabihf': 4.53.5 + '@rollup/rollup-linux-arm64-gnu': 4.53.5 + '@rollup/rollup-linux-arm64-musl': 4.53.5 + '@rollup/rollup-linux-loong64-gnu': 4.53.5 + '@rollup/rollup-linux-ppc64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-musl': 4.53.5 + '@rollup/rollup-linux-s390x-gnu': 4.53.5 + '@rollup/rollup-linux-x64-gnu': 4.53.5 + '@rollup/rollup-linux-x64-musl': 4.53.5 + '@rollup/rollup-openharmony-arm64': 4.53.5 + '@rollup/rollup-win32-arm64-msvc': 4.53.5 + '@rollup/rollup-win32-ia32-msvc': 4.53.5 + '@rollup/rollup-win32-x64-gnu': 4.53.5 + '@rollup/rollup-win32-x64-msvc': 4.53.5 fsevents: 2.3.3 roughjs@4.6.6: @@ -25328,12 +26183,12 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(sass@1.94.2)(webpack@5.103.0(esbuild@0.27.0)): + sass-loader@16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.27.1)): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.94.2 - webpack: 5.103.0(esbuild@0.27.0) + sass: 1.95.0 + webpack: 5.103.0(esbuild@0.27.1) sass@1.94.2: dependencies: @@ -25343,6 +26198,14 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.1 + sass@1.95.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + saucelabs@9.0.2: dependencies: change-case: 4.1.2 @@ -25397,7 +26260,7 @@ snapshots: tmp: 0.0.30 xml2js: 0.4.23 - selenium-webdriver@4.38.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): + selenium-webdriver@4.39.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@bazel/runfiles': 6.5.0 jszip: 3.10.1 @@ -25576,6 +26439,17 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + shiki@3.20.0: + dependencies: + '@shikijs/core': 3.20.0 + '@shikijs/engine-javascript': 3.20.0 + '@shikijs/engine-oniguruma': 3.20.0 + '@shikijs/langs': 3.20.0 + '@shikijs/themes': 3.20.0 + '@shikijs/types': 3.20.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -25725,11 +26599,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.0)): + source-map-loader@5.0.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) source-map-support@0.4.18: dependencies: @@ -26087,7 +26961,36 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1): + tailwindcss@3.4.19(tsx@4.20.6)(yaml@2.8.2): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.2) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.11 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + optional: true + + tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -26106,7 +27009,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -26163,15 +27066,6 @@ snapshots: - bare-abort-controller - react-native-b4a - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - tar@7.5.2: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -26208,16 +27102,16 @@ snapshots: ansi-escapes: 7.2.0 supports-hyperlinks: 3.2.0 - terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.103.0(esbuild@0.27.0)): + terser-webpack-plugin@5.3.14(esbuild@0.27.1)(webpack@5.103.0(esbuild@0.27.1)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.1 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) optionalDependencies: - esbuild: 0.27.0 + esbuild: 0.27.1 terser@5.44.1: dependencies: @@ -26484,6 +27378,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.21.0: + dependencies: + esbuild: 0.27.1 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + tuf-js@4.0.0: dependencies: '@tufjs/models': 4.0.0 @@ -26752,6 +27653,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.2(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + update-notifier-cjs@5.1.7(encoding@0.1.13): dependencies: boxen: 5.1.2 @@ -26900,46 +27807,101 @@ snapshots: - bare-abort-controller - react-native-b4a - vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.3 + rollup: 4.53.5 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 20.19.25 fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 - sass: 1.94.2 + sass: 1.95.0 terser: 5.44.1 tsx: 4.20.6 - yaml: 2.8.1 + yaml: 2.8.2 + optional: true - vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.3 + rollup: 4.53.5 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.1 fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 - sass: 1.94.2 + sass: 1.95.0 + terser: 5.44.1 + tsx: 4.21.0 + yaml: 2.8.2 + + vite@7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.4 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + sass: 1.95.0 + terser: 5.44.1 + tsx: 4.21.0 + yaml: 2.8.2 + + vite@7.2.7(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.25 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + sass: 1.95.0 terser: 5.44.1 tsx: 4.20.6 - yaml: 2.8.1 + yaml: 2.8.2 + + vite@7.2.7(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.4 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + sass: 1.95.0 + terser: 5.44.1 + tsx: 4.21.0 + yaml: 2.8.2 - vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@20.19.25)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@20.19.25)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.13 - '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.13 '@vitest/runner': 4.0.13 '@vitest/snapshot': 4.0.13 @@ -26956,12 +27918,12 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 20.19.25 - jsdom: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + jsdom: 27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti - less @@ -26977,10 +27939,10 @@ snapshots: - yaml optional: true - vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.13 - '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.13 '@vitest/runner': 4.0.13 '@vitest/snapshot': 4.0.13 @@ -26997,11 +27959,51 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 24.10.1 + jsdom: 27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.13 + '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.13 + '@vitest/runner': 4.0.13 + '@vitest/snapshot': 4.0.13 + '@vitest/spy': 4.0.13 + '@vitest/utils': 4.0.13 + debug: 4.4.3(supports-color@10.2.2) + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 24.10.4 jsdom: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti @@ -27017,6 +28019,47 @@ snapshots: - tsx - yaml + vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.13 + '@vitest/mocker': 4.0.13(vite@7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.13 + '@vitest/runner': 4.0.13 + '@vitest/snapshot': 4.0.13 + '@vitest/spy': 4.0.13 + '@vitest/utils': 4.0.13 + debug: 4.4.3(supports-color@10.2.2) + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.4(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 24.10.4 + jsdom: 27.3.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + optional: true + void-elements@2.0.1: {} vrsource-tslint-rules@6.0.0(tslint@6.1.3(typescript@5.9.3))(typescript@5.9.3): @@ -27024,7 +28067,7 @@ snapshots: tslint: 6.1.3(typescript@5.9.3) typescript: 5.9.3 - vscode-html-languageservice@5.6.0: + vscode-html-languageservice@5.6.1: dependencies: '@vscode/l10n': 0.0.18 vscode-languageserver-textdocument: 1.0.12 @@ -27186,7 +28229,7 @@ snapshots: webidl-conversions@8.0.0: {} - webpack-dev-middleware@7.4.5(webpack@5.103.0(esbuild@0.27.0)): + webpack-dev-middleware@7.4.5(webpack@5.103.0(esbuild@0.27.1)): dependencies: colorette: 2.0.20 memfs: 4.51.0 @@ -27195,9 +28238,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) - webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.0)): + webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.103.0(esbuild@0.27.1)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -27225,10 +28268,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.103.0(esbuild@0.27.0)) + webpack-dev-middleware: 7.4.5(webpack@5.103.0(esbuild@0.27.1)) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) transitivePeerDependencies: - bufferutil - debug @@ -27243,12 +28286,12 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.0)): + webpack-subresource-integrity@5.1.0(webpack@5.103.0(esbuild@0.27.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.103.0(esbuild@0.27.0) + webpack: 5.103.0(esbuild@0.27.1) - webpack@5.103.0(esbuild@0.27.0): + webpack@5.103.0(esbuild@0.27.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -27272,7 +28315,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.27.0)(webpack@5.103.0(esbuild@0.27.0)) + terser-webpack-plugin: 5.3.14(esbuild@0.27.1)(webpack@5.103.0(esbuild@0.27.1)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -27520,7 +28563,7 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.1: {} + yaml@2.8.2: {} yargs-parser@18.1.3: dependencies: @@ -27616,6 +28659,10 @@ snapshots: dependencies: zod: 3.25.76 + zod-to-json-schema@3.25.0(zod@4.1.13): + dependencies: + zod: 4.1.13 + zod@3.25.76: {} zod@4.1.13: {} diff --git a/scripts/build/angular-in-memory-web-api.mts b/scripts/build/angular-in-memory-web-api.mts index 248c7bfc21bf..b081bce017ef 100644 --- a/scripts/build/angular-in-memory-web-api.mts +++ b/scripts/build/angular-in-memory-web-api.mts @@ -31,7 +31,9 @@ export function buildAngularInMemoryWebApiPackage(destDir: string): void { // Ensure the output directory is available. exec(`mkdir -p ${destDir}`); - const bazelBinPath = exec(`${bazelCmd} info bazel-bin`, true); + // TODO: Remove --ignore_all_rc_files flag once a repository can be loaded in bazelrc during info + // commands again. See https://github.com/bazelbuild/bazel/issues/25145 for more context. + const bazelBinPath = exec(`${bazelCmd} --ignore_all_rc_files info bazel-bin`, true); // Copy artifacts to `destDir`, so they can be easier persisted on CI and used by non-bazel // scripts/tests. diff --git a/scripts/build/build-packages-dist.mts b/scripts/build/build-packages-dist.mts index ef2fe2ff9a5f..5ff77275a09e 100755 --- a/scripts/build/build-packages-dist.mts +++ b/scripts/build/build-packages-dist.mts @@ -12,7 +12,6 @@ import {buildAngularInMemoryWebApiPackage} from './angular-in-memory-web-api.mjs import {performDefaultSnapshotBuild} from './package-builder.mjs'; import {buildZoneJsPackage} from './zone-js-builder.mjs'; - // Build the legacy (view engine) npm packages into `dist/packages-dist/`. performDefaultSnapshotBuild(); diff --git a/scripts/build/package-builder.mts b/scripts/build/package-builder.mts index da4790491da3..0be893b45b5a 100644 --- a/scripts/build/package-builder.mts +++ b/scripts/build/package-builder.mts @@ -58,7 +58,9 @@ function buildReleasePackages( // List of targets to build. e.g. "packages/core:npm_package", or "packages/forms:npm_package". const targets = exec(queryPackagesCmd, true).split(/\r?\n/).concat(additionalTargets); const packageNames = getPackageNamesOfTargets(targets); - const bazelBinPath = exec(`${bazelCmd} info bazel-bin`, true); + // TODO: Remove --ignore_all_rc_files flag once a repository can be loaded in bazelrc during info + // commands again. See https://github.com/bazelbuild/bazel/issues/25145 for more context. + const bazelBinPath = exec(`${bazelCmd} --ignore_all_rc_files info bazel-bin`, true); const getBazelOutputPath = (pkgName: string) => { return pkgName === 'language-server' ? join(bazelBinPath, 'vscode-ng-language-service/server/npm_package') diff --git a/scripts/build/zone-js-builder.mts b/scripts/build/zone-js-builder.mts index 9a0f869338d8..37fdd4dc8e2c 100644 --- a/scripts/build/zone-js-builder.mts +++ b/scripts/build/zone-js-builder.mts @@ -31,7 +31,9 @@ export function buildZoneJsPackage(destDir: string): void { // Ensure the output directory is available. exec(`mkdir -p ${destDir}`); - const bazelBinPath = exec(`${bazelCmd} info bazel-bin`, true); + // TODO: Remove --ignore_all_rc_files flag once a repository can be loaded in bazelrc during info + // commands again. See https://github.com/bazelbuild/bazel/issues/25145 for more context. + const bazelBinPath = exec(`${bazelCmd} --ignore_all_rc_files info bazel-bin`, true); // Copy artifacts to `destDir`, so they can be easier persisted on CI and used by non-bazel // scripts/tests. diff --git a/scripts/diff-release-package.mts b/scripts/diff-release-package.mts index 47fa73a1bd32..5ac654b1830a 100644 --- a/scripts/diff-release-package.mts +++ b/scripts/diff-release-package.mts @@ -67,8 +67,10 @@ async function main(packageName: string) { git.run(['clone', '--depth=1', `https://github.com/${snapshotRepoName}.git`, tmpDir]); console.info(`--> Cloned snapshot repo.`); + // TODO: Remove --ignore_all_rc_files flag once a repository can be loaded in bazelrc during info + // commands again. See https://github.com/bazelbuild/bazel/issues/25145 for more context. const bazelBinDir = childProcess - .spawnSync(bazel, ['info', 'bazel-bin'], { + .spawnSync(bazel, ['--ignore_all_rc_files', 'info', 'bazel-bin'], { shell: true, encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit'], diff --git a/tools/manual_api_docs/elements/ng-container.md b/tools/manual_api_docs/elements/ng-container.md index 94d15c7690c8..d9f13ad3ed3f 100644 --- a/tools/manual_api_docs/elements/ng-container.md +++ b/tools/manual_api_docs/elements/ng-container.md @@ -20,26 +20,16 @@ special element we can produce very clean templates easy to understand and work For example, we may want to have a number of elements shown conditionally but they do not need to be all under the same root element. That can be easily done by wrapping them in such a block: - - -<ng-container \*ngIf="condition"> -… -</ng-container> - - +```angular-html + +``` This can also be augmented with an `else` statement alongside an `` as: - - -<ng-container \*ngIf="condition; else templateA"> -… -</ng-container> -<ng-template #templateA> -… -</ng-template> - - +```angular-html + + +``` ### Combination of multiple structural directives @@ -51,30 +41,25 @@ The most common scenario is with `*ngIf` and `*ngFor`. For example, let's imagin list of items but each item needs to be displayed only if a certain condition is true. We could be tempted to try something like: - - -<ul> -<li *ngFor="let item of items" *ngIf="item.isValid"> -{{ item.name }} -</li> -</ul> - - +```angular-html +
      +
    • + {{ item.name }} +
    • +
    +``` As we said that would not work, what we can do is to simply move one of the structural directives to an `` element, which would then wrap the other one, like so: - - -<ul> -<ng-container *ngFor="let item of items"> -<li *ngIf="item.isValid"> -{{ item.name }} -</li> -</ng-container> -</ul> - - +```angular-html +
      + +
    • {{ item.name }} +
    • +
      +
    +``` This would work as intended without introducing any new unnecessary elements in the DOM. @@ -94,25 +79,18 @@ an `NgTemplateOutlet`. Like so: - - -<!-- … --> - -<ng-container \*ngTemplateOutlet="tmpl; context: {$implicit: 'Hello'}"> -</ng-container> - -<!-- … --> - -<ng-container \*ngTemplateOutlet="tmpl; context: {$implicit: 'World'}"> -</ng-container> +```angular-html + -<!-- … --> + -<ng-template #tmpl let-text> -<h1>{{ text }}</h1> -</ng-template> + +

    + {{ text }} +

    +
    -
    +``` For more information regarding `NgTemplateOutlet`, see the [`NgTemplateOutlet`s api documentation page](api/common/NgTemplateOutlet). diff --git a/tools/manual_api_docs/elements/ng-template.md b/tools/manual_api_docs/elements/ng-template.md index 398e9389066a..00f70f9368c8 100644 --- a/tools/manual_api_docs/elements/ng-template.md +++ b/tools/manual_api_docs/elements/ng-template.md @@ -12,11 +12,11 @@ Angular won't render the middle "Hip!" in the phrase "Hip! Hip! Hooray!" because surrounding ``. ```html +

    Hip!

    +

    Hip!

    - -

    Hip!

    -
    -

    Hooray!

    +
    +

    Hooray!

    ```
    diff --git a/tools/symbol-extractor/BUILD.bazel b/tools/symbol-extractor/BUILD.bazel index 9c18bfb873d6..b33f6e33eb11 100644 --- a/tools/symbol-extractor/BUILD.bazel +++ b/tools/symbol-extractor/BUILD.bazel @@ -36,7 +36,7 @@ ts_project( jasmine_test( name = "test", - data = glob(["symbol_extractor_spec/**"]) + [ + data = [ ":test_lib", "//tools/symbol-extractor/symbol_extractor_spec:es2015_class_output", "//tools/symbol-extractor/symbol_extractor_spec:fixtures", diff --git a/vscode-ng-language-service/CHANGELOG.md b/vscode-ng-language-service/CHANGELOG.md index 2a595d353fe9..bea5f16a08a4 100644 --- a/vscode-ng-language-service/CHANGELOG.md +++ b/vscode-ng-language-service/CHANGELOG.md @@ -1,3 +1,11 @@ +## 21.0.1 (2025-12-17) + +- fix(language-service): Prevent language service from crashing on suggestion diagnostic errors (5047be4bc1)[https://github.com/angular/angular/commit/5047be4bc1c6f6016263703c743f8033f669f0ee] +- fix(language-service): avoid interpolation highlighting inside @let (e0694df3ec)[https://github.com/angular/angular/commit/e0694df3eccae3d31a4ea537dffe1db1368ef34a] +- fix(vscode-extension): Show warning if multiple versions of Angular are detected in workspace (a41b0ce025)[https://github.com/angular/angular/commit/a41b0ce02528c27e4804bcd39a61c932503bae61] + + + ## 21.0.0 (2025-11-18) - fix(language-service): address potential memory leak during project creation (89095946cf)[https://github.com/angular/angular/commit/89095946cff051c5613b8f54ec722d08cd47c709] diff --git a/vscode-ng-language-service/README.md b/vscode-ng-language-service/README.md index 5280f1ffc3fc..6c971ab68d89 100644 --- a/vscode-ng-language-service/README.md +++ b/vscode-ng-language-service/README.md @@ -51,7 +51,7 @@ For more information, please see [#594](https://github.com/angular/vscode-ng-lan ## Installing a particular release build -Download the `.vsix` file for the release that you want to install from the [releases](https://github.com/angular/vscode-ng-language-service/releases) tab. +Download the `.vsix` file for the release that you want to install from the [releases](https://github.com/angular/angular/releases?q=vscode&expanded=true) tab. _Do not open the .vsix file directly_. Instead, in Visual Studio code, go to the extensions tab. Click on the "..." menu in the upper right corner of the extensions tab, select "Install from vsix..." and then select the .vsix file for the release you just downloaded. diff --git a/vscode-ng-language-service/client/src/client.ts b/vscode-ng-language-service/client/src/client.ts index 4751aa4c3197..34ba596383d1 100644 --- a/vscode-ng-language-service/client/src/client.ts +++ b/vscode-ng-language-service/client/src/client.ts @@ -419,13 +419,7 @@ export class AngularLanguageClient implements vscode.Disposable { } } - // Pass the earliest Angular version along to the compiler for maximum compatibility. - if (angularVersions.length > 0) { - args.push('--angularCoreVersion', angularVersions[0].version.toString()); - this.outputChannel.appendLine( - `Using Angular version ${angularVersions[0].version.toString()}.`, - ); - } + setAngularVersionAndShowMultipleVersionsWarning(angularVersions, args, this.outputChannel); const forceStrictTemplates = config.get('angular.forceStrictTemplates'); if (forceStrictTemplates) { @@ -674,3 +668,35 @@ async function getAngularVersionsInWorkspace( } return Array.from(angularCoreModules); } + +function setAngularVersionAndShowMultipleVersionsWarning( + angularVersions: NodeModule[], + args: string[], + outputChannel: vscode.OutputChannel, +) { + if (angularVersions.length === 0) { + return; + } + // Pass the earliest Angular version along to the compiler for maximum compatibility. + // For example, if we tell the v21 compiler that we're using v21 but there's a v13 project, + // the compiler may attempt to import and use APIs from angular core that don't exist in v13. + args.push('--angularCoreVersion', angularVersions[0].version.toString()); + outputChannel.appendLine(`Using Angular version ${angularVersions[0].version.toString()}.`); + + let minorVersions = new Map(); + for (const v of angularVersions) { + minorVersions.set(`${v.version.major}.${v.version.minor}`, v); + } + if (minorVersions.size > 1) { + vscode.window.showWarningMessage( + `Multiple versions of Angular detected in the workspace. This can lead to compatibility issues for the language service. ` + + `See the output panel for more details.`, + ); + outputChannel.appendLine(`Multiple Angular versions detected in the workspace:`); + for (const v of minorVersions.values()) { + outputChannel.appendLine( + ` Angular version ${v.version.toString()} detected at ${v.resolvedPath}`, + ); + } + } +} diff --git a/vscode-ng-language-service/integration/e2e/definition_spec.ts b/vscode-ng-language-service/integration/e2e/definition_spec.ts index 6264bd2ef5b6..a9bb3fba6f3e 100644 --- a/vscode-ng-language-service/integration/e2e/definition_spec.ts +++ b/vscode-ng-language-service/integration/e2e/definition_spec.ts @@ -14,9 +14,9 @@ describe('Angular LS', () => { it(`returns definition for variable in template`, async () => { // vscode Position is zero-based - // template: `

    Hello {{name}}

    `, - // ^-------- here - const position = new vscode.Position(4, 25); + // template: `

    Hello {{ name }}

    `, + // ^-------- here + const position = new vscode.Position(4, 26); // For a complete list of standard commands, see // https://code.visualstudio.com/api/references/commands const definitions = await vscode.commands.executeCommand( diff --git a/vscode-ng-language-service/integration/lsp/ivy_spec.ts b/vscode-ng-language-service/integration/lsp/ivy_spec.ts index 4881a4fe9ee9..792adabc25bc 100644 --- a/vscode-ng-language-service/integration/lsp/ivy_spec.ts +++ b/vscode-ng-language-service/integration/lsp/ivy_spec.ts @@ -76,7 +76,7 @@ describe('Angular language server', () => { textDocument: { uri: APP_COMPONENT_URI, }, - position: {line: 4, character: 25}, + position: {line: 4, character: 26}, }); expect(response?.contents).toContain({ language: 'typescript', @@ -453,11 +453,11 @@ export class AppComponent { textDocument: { uri: APP_COMPONENT_URI, }, - position: {line: 4, character: 25}, + position: {line: 4, character: 26}, })) as {range: lsp.Range; placeholder: string}; expect(response.range).toEqual({ - start: {line: 4, character: 25}, - end: {line: 4, character: 29}, + start: {line: 4, character: 26}, + end: {line: 4, character: 30}, }); expect(response.placeholder).toEqual('name'); }); @@ -472,8 +472,8 @@ export class AppComponent { }; const expectedRenameInTemplate = { range: { - start: {line: 4, character: 25}, - end: {line: 4, character: 29}, + start: {line: 4, character: 26}, + end: {line: 4, character: 30}, }, newText: 'surname', }; @@ -483,7 +483,7 @@ export class AppComponent { textDocument: { uri: APP_COMPONENT_URI, }, - position: {line: 4, character: 25}, + position: {line: 4, character: 26}, newName: 'surname', }); expect(response).not.toBeNull(); diff --git a/vscode-ng-language-service/integration/pre_standalone_project/app/app.component.ts b/vscode-ng-language-service/integration/pre_standalone_project/app/app.component.ts index 582265a2f36e..6d6b0cc6d8ab 100644 --- a/vscode-ng-language-service/integration/pre_standalone_project/app/app.component.ts +++ b/vscode-ng-language-service/integration/pre_standalone_project/app/app.component.ts @@ -2,7 +2,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core'; @Component({ selector: 'my-app', - template: `

    Hello {{name}}

    `, + template: `

    Hello {{ name }}

    `, }) export class AppComponent { name = 'Angular'; diff --git a/vscode-ng-language-service/integration/project/app/app.component.ts b/vscode-ng-language-service/integration/project/app/app.component.ts index 234149508263..97d0ad9a7404 100644 --- a/vscode-ng-language-service/integration/project/app/app.component.ts +++ b/vscode-ng-language-service/integration/project/app/app.component.ts @@ -2,7 +2,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core'; @Component({ selector: 'my-app', - template: `

    Hello {{name}}

    `, + template: `

    Hello {{ name }}

    `, standalone: false, }) export class AppComponent { diff --git a/vscode-ng-language-service/integration/project/app/bar.component.ts b/vscode-ng-language-service/integration/project/app/bar.component.ts index 9d67660bcaac..70b0bda36980 100644 --- a/vscode-ng-language-service/integration/project/app/bar.component.ts +++ b/vscode-ng-language-service/integration/project/app/bar.component.ts @@ -2,7 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'baz-component', - template: `

    Hello {{name}}

    `, + template: `

    Hello {{ name }}

    `, standalone: true, }) export class BazComponent { diff --git a/vscode-ng-language-service/integration/project/package.json b/vscode-ng-language-service/integration/project/package.json index 2709ba9596fd..b790bc67c226 100644 --- a/vscode-ng-language-service/integration/project/package.json +++ b/vscode-ng-language-service/integration/project/package.json @@ -2,10 +2,10 @@ "name": "angular-ls-integration-test-project", "private": true, "dependencies": { - "@angular/common": "21.1.0-next.0", - "@angular/compiler": "21.1.0-next.0", - "@angular/compiler-cli": "21.1.0-next.0", - "@angular/core": "21.1.0-next.0", + "@angular/common": "21.1.0-next.4", + "@angular/compiler": "21.1.0-next.4", + "@angular/compiler-cli": "21.1.0-next.4", + "@angular/core": "21.1.0-next.4", "rxjs": "7.8.2" }, "devDependencies": { diff --git a/vscode-ng-language-service/package.json b/vscode-ng-language-service/package.json index 503909e0bd0d..ffe26cbf61b8 100644 --- a/vscode-ng-language-service/package.json +++ b/vscode-ng-language-service/package.json @@ -270,8 +270,8 @@ "@types/vscode": "^1.74.3", "@vscode/test-electron": "~2.5.2", "@vscode/vsce": "~3.7.0", - "jasmine": "~5.12.0", - "jasmine-core": "~5.12.0", + "jasmine": "~5.13.0", + "jasmine-core": "~5.13.0", "jasmine-reporters": "~2.5.2", "source-map-support": "^0.5.21", "vscode-jsonrpc": "6.0.0", diff --git a/vscode-ng-language-service/server/package.json b/vscode-ng-language-service/server/package.json index 9dd81424dd8b..2d2e890ad803 100644 --- a/vscode-ng-language-service/server/package.json +++ b/vscode-ng-language-service/server/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@types/node": "^24.5.2", - "vscode-html-languageservice": "5.6.0", + "vscode-html-languageservice": "5.6.1", "vscode-languageserver": "7.0.0", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "3.1.0" diff --git a/vscode-ng-language-service/syntaxes/src/template.ts b/vscode-ng-language-service/syntaxes/src/template.ts index 0fd061cc7556..e88d43fd4ac2 100644 --- a/vscode-ng-language-service/syntaxes/src/template.ts +++ b/vscode-ng-language-service/syntaxes/src/template.ts @@ -10,7 +10,7 @@ import {GrammarDefinition} from './types'; export const Template: GrammarDefinition = { scopeName: 'template.ng', - injectionSelector: 'L:text.html -comment -control.block.ng', + injectionSelector: 'L:text.html -comment -control.block.ng -meta.definition.variable.ng', patterns: [{include: '#interpolation'}], repository: { interpolation: { diff --git a/vscode-ng-language-service/syntaxes/template.json b/vscode-ng-language-service/syntaxes/template.json index c81b0e488f53..4714634caf27 100644 --- a/vscode-ng-language-service/syntaxes/template.json +++ b/vscode-ng-language-service/syntaxes/template.json @@ -1,6 +1,6 @@ { "scopeName": "template.ng", - "injectionSelector": "L:text.html -comment -control.block.ng", + "injectionSelector": "L:text.html -comment -control.block.ng -meta.definition.variable.ng", "patterns": [ { "include": "#interpolation"