mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-16 18:13:06 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a7a59cea7 | ||
|
|
762eba8490 | ||
|
|
62d7d9084d | ||
|
|
6f414ae98e | ||
|
|
c7c2ba292e | ||
|
|
84e710191d | ||
|
|
a776cd02c0 | ||
|
|
2c1405d053 | ||
|
|
0bc823beea | ||
|
|
89053a1b9d | ||
|
|
68d382a401 | ||
|
|
ddfabc0e44 |
@@ -26,6 +26,24 @@ runs:
|
||||
echo "TAG_NPM not set, aborting"
|
||||
exit 1
|
||||
fi
|
||||
ADF_VERSION=$(npm view @alfresco/adf-core@${TAG_NPM} version)
|
||||
|
||||
# Retry to absorb npm registry propagation delay right after publish.
|
||||
NPM_VIEW_RETRIES=24
|
||||
NPM_VIEW_RETRY_DELAY=20
|
||||
for attempt in $(seq 1 "$NPM_VIEW_RETRIES"); do
|
||||
ADF_VERSION=$(npm view @alfresco/adf-core@${TAG_NPM} version 2>/dev/null) || true
|
||||
if [[ -n $ADF_VERSION ]]; then
|
||||
break
|
||||
fi
|
||||
if [[ $attempt -lt $NPM_VIEW_RETRIES ]]; then
|
||||
sleep "$NPM_VIEW_RETRY_DELAY"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $ADF_VERSION ]]; then
|
||||
echo "Could not resolve @alfresco/adf-core@${TAG_NPM} version, aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check bundle on TAG_NPM='${TAG_NPM}' and ADF_VERSION='${ADF_VERSION}'"
|
||||
./scripts/github/build/npm-check-bundles.sh -v ${ADF_VERSION}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"entries": {
|
||||
"github/gh-aw-actions/setup@v0.87.10": {
|
||||
"github/gh-aw-actions/setup@v0.88.7": {
|
||||
"repo": "github/gh-aw-actions/setup",
|
||||
"version": "v0.87.10",
|
||||
"sha": "bc8c008a419c5b7a29df6f5641edd35fd1c6ea85"
|
||||
"version": "v0.88.7",
|
||||
"sha": "5e508589e03a7757a7e05b26e834292f5445bfb6"
|
||||
}
|
||||
},
|
||||
"containers": {
|
||||
@@ -12,20 +12,45 @@
|
||||
"digest": "sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/agent:0.28.14": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/agent:0.28.14",
|
||||
"digest": "sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44",
|
||||
"digest": "sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14",
|
||||
"digest": "sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/squid:0.27.44": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/squid:0.27.44",
|
||||
"digest": "sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/squid:0.28.14": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/squid:0.28.14",
|
||||
"digest": "sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-mcpg:v0.4.18": {
|
||||
"image": "ghcr.io/github/gh-aw-mcpg:v0.4.18",
|
||||
"digest": "sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-node": {
|
||||
"image": "ghcr.io/github/gh-aw-node",
|
||||
"digest": "sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"
|
||||
"digest": "sha256:87366cb93b06d7a4e3db08a705875efc027b6c394da336119e7a067abacbb39b",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-node@sha256:87366cb93b06d7a4e3db08a705875efc027b6c394da336119e7a067abacbb39b"
|
||||
},
|
||||
"ghcr.io/github/github-mcp-server:v1.11.0": {
|
||||
"image": "ghcr.io/github/github-mcp-server:v1.11.0",
|
||||
"digest": "sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699",
|
||||
"pinned_image": "ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
ref: develop
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: Pull translations from Crowdin
|
||||
uses: crowdin/github-action@8f01d54f70f1713ee3f09d82c2bbb2daeac28689 # v2.17.1
|
||||
uses: crowdin/github-action@0d5670f539973aea2f01abce61a8989934df0025 # v3.0.2
|
||||
with:
|
||||
skip_ref_checkout: true
|
||||
upload_sources: false
|
||||
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
|
||||
npm-check-bundle:
|
||||
needs: [release-npm]
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 25
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Push Source Files to Crowdin
|
||||
uses: crowdin/github-action@8f01d54f70f1713ee3f09d82c2bbb2daeac28689 # v2.17.1
|
||||
uses: crowdin/github-action@0d5670f539973aea2f01abce61a8989934df0025 # v3.0.2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_sources_args: --delete-obsolete
|
||||
|
||||
+122
-65
File diff suppressed because one or more lines are too long
@@ -23,6 +23,8 @@ network:
|
||||
- api.osv.dev
|
||||
- api.scorecard.dev
|
||||
- search.maven.org
|
||||
- api.github.com
|
||||
- github.com
|
||||
|
||||
safe-outputs:
|
||||
add-comment:
|
||||
@@ -33,8 +35,11 @@ safe-outputs:
|
||||
remove-labels:
|
||||
allowed: [security:low, security:medium, security:high]
|
||||
submit-pull-request-review:
|
||||
allowed-events: [COMMENT, REQUEST_CHANGES]
|
||||
supersede-older-reviews: true
|
||||
dismiss-pull-request-review:
|
||||
|
||||
source: Alfresco/alfresco-build-tools/.github/workflows/supply-chain-review.md@e35840d877477896b1f0aa05d05371cb3b31ce9f
|
||||
source: Alfresco/alfresco-build-tools/.github/workflows/supply-chain-review.md@599eebd2a1b84e76d540e41036520df3a64c7cbd
|
||||
---
|
||||
|
||||
# Supply Chain Review
|
||||
@@ -57,7 +62,7 @@ For each changed dependency extract:
|
||||
- Old version (or mark as `NEW DEPENDENCY` if newly added)
|
||||
- New version
|
||||
|
||||
If no dependency files were changed, post a brief PR comment stating that no dependency changes were detected and no review is needed, then stop.
|
||||
If no dependency files were changed, post a brief PR comment stating that no dependency changes were detected, then go directly to Step 6 — treating this as LOW risk — to remove any stale `security:*` labels and submit the required pull request review, then stop.
|
||||
|
||||
## Step 1b — Filter Internal Dependencies
|
||||
|
||||
@@ -74,7 +79,7 @@ For each internal dependency found:
|
||||
2. Record the package name (with `@` replaced by `(at)` for GitHub comment compatibility), ecosystem, old version, and new version in a separate "Internal Dependencies (Skipped)" list.
|
||||
3. Continue with Step 2 only for the remaining external/public dependencies.
|
||||
|
||||
If ALL changed dependencies are internal, skip Steps 2-4 and proceed directly to Step 5, posting a report that lists the internal dependencies and notes that no external supply chain analysis was performed.
|
||||
If ALL changed dependencies are internal, skip Steps 2-4 and proceed directly to Step 5 — treating this as LOW risk for Step 6 — posting a report that lists the internal dependencies and notes that no external supply chain analysis was performed.
|
||||
|
||||
## Step 2 — Collect Data for Each Dependency
|
||||
|
||||
@@ -268,7 +273,7 @@ Verify that the source repository URL in registry metadata points to the canonic
|
||||
Assign a risk score (0-100) to each dependency using these guidelines:
|
||||
|
||||
| Priority | Signal | Typical Impact |
|
||||
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
||||
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| Highest | Known CRITICAL/HIGH CVEs in new version, confirmed typosquatting, malicious code in diff, build provenance mismatch (tag points to different code than published artifact), tag mimicry on fork | 60+ points |
|
||||
| High | Maintainer takeover pattern (publisher changed + old maintainers removed), dangerous install scripts, known compromised package, moved/recreated tag with different commit, provenance attestations removed from package that previously had them | 20-40 points |
|
||||
| Medium | Low OpenSSF Scorecard (< 3), publisher changed (without full takeover), new install scripts, very recent publish (< 48h), obfuscated code in diff, unsigned lightweight tags on security-critical packages, absence of provenance on high-profile packages | 10-20 points |
|
||||
@@ -304,9 +309,9 @@ GitHub enforces a maximum of 10 mentions per comment. Package names containing `
|
||||
|
||||
### Internal Dependencies (Skipped)
|
||||
|
||||
| Package | Ecosystem | Old Version | New Version | Reason |
|
||||
|-------------------|-----------|-------------|-------------|---------------------------------|
|
||||
| (at)hyland/core | npm | 3.1.0 | 3.2.0 | Internal ((at)hyland/* scope) |
|
||||
| Package | Ecosystem | Old Version | New Version | Reason |
|
||||
|-----------------|-----------|-------------|-------------|-------------------------------|
|
||||
| (at)hyland/core | npm | 3.1.0 | 3.2.0 | Internal ((at)hyland/* scope) |
|
||||
|
||||
_These dependencies are internal packages not available on public registries. External API checks were skipped._
|
||||
|
||||
@@ -342,18 +347,42 @@ No suspicious patterns detected. Routine upgrade.
|
||||
|
||||
## Step 6 — Apply Label and Review Status
|
||||
|
||||
- First, remove any `security:low`, `security:medium`, or `security:high` labels already present on the PR from a previous review — this PR may have been reviewed before (e.g., after a new commit), and stale risk labels must not remain alongside the new one.
|
||||
- Then apply a label to the PR based on the highest risk level found:
|
||||
- `security:low` for LOW risk
|
||||
- `security:medium` for MEDIUM risk
|
||||
- `security:high` for HIGH or CRITICAL risk
|
||||
- If the highest risk level is HIGH or CRITICAL, submit a pull request review requesting changes, with a summary of the critical findings.
|
||||
- If the risk is MEDIUM, submit a pull request review as a comment, noting that human review is recommended.
|
||||
- If the risk is LOW, do not submit a review — the PR comment is sufficient.
|
||||
### 6a. Labels — order-independent update
|
||||
|
||||
Determine the single target label for the highest risk level found: `security:low`, `security:medium`, or `security:high`.
|
||||
|
||||
- Remove only the OTHER `security:*` labels (the ones that do NOT match the target) if present on the PR — this clears stale risk labels left by a previous review (e.g., after a new commit changed the risk level).
|
||||
- Add the target label if it is not already present.
|
||||
- **Never remove the target label itself.** Because the remove and add operations act on disjoint labels, the final state is correct regardless of which of the two safe-output calls (`add_labels` / `remove_labels`) happens to be processed first — do NOT rely on emitting them in a particular order, since that ordering is not guaranteed. (Do not, for example, remove all three `security:*` labels and then add the target back — if the removal is processed after the add, the target label would be stripped again, leaving the PR with no risk label at all.)
|
||||
|
||||
### 6b. Dismiss stale reviews from this workflow
|
||||
|
||||
Every review this workflow posts (see 6c) MUST start its body with the exact literal marker line `**Supply Chain Review**` as the first line, so future runs can recognize their own prior reviews.
|
||||
|
||||
Before posting the new review:
|
||||
|
||||
1. Fetch the PR's existing reviews (GitHub MCP `pull_requests` toolset).
|
||||
2. Identify any review that is authored by this workflow's actor AND whose body starts with the `**Supply Chain Review**` marker AND is still in the `CHANGES_REQUESTED` state — that is a stale review from an earlier run of this same workflow (e.g., posted before the flagged dependency was fixed, downgraded, or removed).
|
||||
3. For each such review, call `dismiss_pull_request_review` with its explicit numeric `review_id` (do NOT use `'auto'` — this repository may run other agentic workflows that also post as the same actor, and `'auto'` would dismiss their reviews too) and a justification of at least 20 characters (e.g., "Superseded by a newer Supply Chain Review run.").
|
||||
|
||||
Do this even though `submit-pull-request-review` is also configured with `supersede-older-reviews: true` — that setting is best-effort and may not always recognize the prior review, so the explicit dismissal above is the reliable mechanism and must always be attempted.
|
||||
|
||||
### 6c. Submit the review
|
||||
|
||||
**Always submit a pull request review — in every invocation, with no exceptions.** This is not conditional on risk level. Submit a review even when there are no dependency changes, when all dependencies are internal, or when risk is LOW — skipping it would mean a stale `REQUEST_CHANGES` review from an earlier run is never replaced or dismissed.
|
||||
|
||||
The review body must start with the `**Supply Chain Review**` marker line (see 6b), followed by the assessment:
|
||||
|
||||
- If the highest risk level is HIGH or CRITICAL, submit the review as **request changes**, with a summary of the critical findings.
|
||||
- If the risk is MEDIUM, submit the review as a **comment**, noting that human review is recommended.
|
||||
- If the risk is LOW (including when there are no dependency changes, or all changed dependencies are internal), submit the review as a **comment**, summarizing that no concerns were found and the PR comment has the full detail.
|
||||
- **Never submit the review as an approval, under any circumstance** — this workflow only ever comments or requests changes; a human always makes the merge decision.
|
||||
|
||||
## Important Guidelines
|
||||
|
||||
- **Never approve or merge the PR** — all actions are advisory or blocking only. A human always makes the merge decision.
|
||||
- **Never approve or merge the PR** — all actions are advisory or blocking only. A human always makes the merge decision. Every review this workflow submits must use the comment or request-changes event — never the approve event.
|
||||
- **Always submit exactly one pull request review per invocation, regardless of outcome**, and always prefix its body with the `**Supply Chain Review**` marker — this is required so that a later run of this same workflow can find and dismiss it via `dismiss_pull_request_review` once it becomes stale (see Step 6b). Do not rely on `supersede-older-reviews` alone; it is best-effort.
|
||||
- **Never remove the `security:*` label matching the current risk level** when clearing stale labels — only remove the other ones, so the final label state is correct no matter which safe-output call is processed first (see Step 6a).
|
||||
- Be specific in findings — cite exact data (vulnerability ID, maintainer name, script content, file path, API response) rather than vague warnings.
|
||||
- For Maven packages, adapt npm-specific checks appropriately (e.g., install scripts become build plugin analysis, maintainer metadata may be limited).
|
||||
- When a package is a NEW dependency (no old version), pay extra attention to project health, name legitimacy, and install scripts since there is no historical baseline to compare against.
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<div class="adf-search-logical-filter-container">
|
||||
<div *ngFor="let field of fields" class="adf-search-input">
|
||||
<mat-label data-automation-id="adf-search-input-label">{{('SEARCH.LOGICAL_SEARCH.' + field + '_LABEL') | translate}}</mat-label>
|
||||
<mat-label [id]="`adf-search-input-label-${field}`" data-automation-id="adf-search-input-label">{{('SEARCH.LOGICAL_SEARCH.' + field + '_LABEL') | translate}}</mat-label>
|
||||
<input type="text"
|
||||
[(ngModel)]="searchCondition[LogicalSearchFields[field]]"
|
||||
placeholder="{{ ('SEARCH.LOGICAL_SEARCH.' + field + '_HINT') | translate }}"
|
||||
[attr.aria-label]="('SEARCH.LOGICAL_SEARCH.' + field + '_HINT') | translate"/>
|
||||
[attr.aria-labelledby]="`adf-search-input-label-${field}`"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+16
@@ -19,10 +19,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { LogicalSearchCondition, LogicalSearchFields, SearchLogicalFilterComponent } from './search-logical-filter.component';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { UnitTestingUtils } from '@alfresco/adf-core';
|
||||
|
||||
describe('SearchLogicalFilterComponent', () => {
|
||||
let component: SearchLogicalFilterComponent;
|
||||
let fixture: ComponentFixture<SearchLogicalFilterComponent>;
|
||||
let unitTestingUtils: UnitTestingUtils;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -41,6 +43,7 @@ describe('SearchLogicalFilterComponent', () => {
|
||||
execute: jasmine.createSpy('execute')
|
||||
} as any;
|
||||
component.settings = { field: 'field1,field2', allowUpdateOnChange: true, hideDefaultAction: false };
|
||||
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -204,4 +207,17 @@ describe('SearchLogicalFilterComponent', () => {
|
||||
expect(component.searchCondition).toEqual({ matchAll: 'test', matchAny: 'test2', matchExact: '', exclude: '' });
|
||||
expect(component.context.filterLoaded.next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should use aria-labelledby attribute for input fields', () => {
|
||||
const inputs = getInputs();
|
||||
const fieldset = unitTestingUtils.getAllByDataAutomationId('adf-search-input-label');
|
||||
|
||||
inputs.forEach((input, index) => {
|
||||
const fieldsetId = fieldset[index].nativeElement.getAttribute('id');
|
||||
expect(input.getAttribute('aria-labelledby')).toBe(fieldsetId);
|
||||
expect(input.hasAttribute('aria-label')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { UnitTestingUtils } from '../../testing';
|
||||
import { FormRulesManager } from '../models/form-rules.model';
|
||||
import { FORM_RULES_MANAGER, FormRulesManager } from '../models/form-rules.model';
|
||||
import { FormRulesEvent } from '../events/form-rules.event';
|
||||
import { FormRenderingService } from '../services/form-rendering.service';
|
||||
import { FormService } from '../services/form.service';
|
||||
import { FormRendererComponent } from './form-renderer.component';
|
||||
@@ -52,6 +53,7 @@ import {
|
||||
import { FormFieldModel, FormModel, TextWidgetComponent } from './widgets';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { of } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { WidgetVisibilityService } from '../services/widget-visibility.service';
|
||||
|
||||
const typeIntoInput = (testingUtils: UnitTestingUtils, selector: string, message: string) => {
|
||||
@@ -137,6 +139,38 @@ describe('Form Renderer Component', () => {
|
||||
});
|
||||
|
||||
describe('visibility refresh on form rules', () => {
|
||||
const buildFormWithRequiredField = (startVisible: boolean = true): FormModel => {
|
||||
const form = new FormModel(
|
||||
{
|
||||
id: 'required-field-form',
|
||||
fields: [
|
||||
{
|
||||
id: 'container',
|
||||
type: 'container',
|
||||
numberOfColumns: 1,
|
||||
fields: { 1: [{ id: 'requiredText', type: 'text', name: 'Required text', required: true }] }
|
||||
}
|
||||
]
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
formService
|
||||
);
|
||||
|
||||
if (!startVisible) {
|
||||
form.changeFieldVisibility('requiredText', false);
|
||||
form.validateForm();
|
||||
}
|
||||
|
||||
return form;
|
||||
};
|
||||
|
||||
const hideFieldOnFieldValueChanged = (form: FormModel, fieldId: string, visible: boolean): void => {
|
||||
formService.formRulesEvent
|
||||
.pipe(filter((event) => event?.type === 'fieldValueChanged' && event.form?.id === form.id))
|
||||
.subscribe(() => form.changeFieldVisibility(fieldId, visible));
|
||||
};
|
||||
|
||||
it('should refresh visibility when a fieldValueChanged rule event fires for the same form', () => {
|
||||
const form = formService.parseForm(textWidgetVisibility.formRepresentation);
|
||||
formRendererComponent.formDefinition = form;
|
||||
@@ -176,6 +210,80 @@ describe('Form Renderer Component', () => {
|
||||
|
||||
expect(refreshVisibilitySpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should revalidate the form when a rule hides an empty required field', () => {
|
||||
const form = buildFormWithRequiredField();
|
||||
formRendererComponent.formDefinition = form;
|
||||
hideFieldOnFieldValueChanged(form, 'requiredText', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.isValid).toBe(false, 'form should start invalid with a visible empty required field');
|
||||
|
||||
formService.formRulesEvent.next({ type: 'fieldValueChanged', form } as any);
|
||||
|
||||
expect(form.isValid).toBe(true, 'hiding the required field should re-enable the outcome');
|
||||
});
|
||||
|
||||
it('should revalidate the form when a rule reveals an empty required field', () => {
|
||||
const form = buildFormWithRequiredField(false);
|
||||
formRendererComponent.formDefinition = form;
|
||||
hideFieldOnFieldValueChanged(form, 'requiredText', true);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.isValid).toBe(true, 'form should start valid while the required field is hidden');
|
||||
|
||||
formService.formRulesEvent.next({ type: 'fieldValueChanged', form } as any);
|
||||
|
||||
expect(form.isValid).toBe(false, 'revealing the empty required field should disable the outcome');
|
||||
});
|
||||
|
||||
it('should revalidate the form when a rule hides a group containing an empty required field', () => {
|
||||
const form = new FormModel(
|
||||
{
|
||||
id: 'required-group-form',
|
||||
fields: [
|
||||
{
|
||||
id: 'requiredGroup',
|
||||
type: 'group',
|
||||
params: {},
|
||||
numberOfColumns: 1,
|
||||
fields: { 1: [{ id: 'textInGroup', type: 'text', name: 'Text', required: true }] }
|
||||
}
|
||||
]
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
formService
|
||||
);
|
||||
form.enableParentVisibilityCheck = true;
|
||||
form.getFormFields().forEach((field) => (field.checkParentVisibilityForValidation = true));
|
||||
form.validateForm();
|
||||
|
||||
formRendererComponent.formDefinition = form;
|
||||
hideFieldOnFieldValueChanged(form, 'requiredGroup', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.isValid).toBe(false, 'form should start invalid while the group is visible');
|
||||
|
||||
formService.formRulesEvent.next({ type: 'fieldValueChanged', form } as any);
|
||||
|
||||
expect(form.isValid).toBe(true, 'hiding the group should exempt its required child');
|
||||
});
|
||||
|
||||
it('should revalidate the form for rule events other than fieldValueChanged', () => {
|
||||
const form = buildFormWithRequiredField();
|
||||
formRendererComponent.formDefinition = form;
|
||||
formService.formRulesEvent
|
||||
.pipe(filter((event) => event?.type === 'onRowCountChanged' && event.form?.id === form.id))
|
||||
.subscribe(() => form.changeFieldVisibility('requiredText', false));
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.isValid).toBe(false, 'form should start invalid with a visible empty required field');
|
||||
|
||||
formService.formRulesEvent.next({ type: 'onRowCountChanged', form } as any);
|
||||
|
||||
expect(form.isValid).toBe(true, 'row-count driven rules should also recompute validity');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Display Date Widget ', () => {
|
||||
@@ -1225,3 +1333,67 @@ describe('Form Renderer Component', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class HideOnFieldValueChangedRulesManager extends FormRulesManager<{ targetFieldId: string }> {
|
||||
protected getRules(): { targetFieldId: string } {
|
||||
return { targetFieldId: 'requiredText' };
|
||||
}
|
||||
|
||||
protected handleRuleEvent(event: FormRulesEvent, rules: { targetFieldId: string }): void {
|
||||
if (event.type === 'fieldValueChanged') {
|
||||
this.formModel.changeFieldVisibility(rules.targetFieldId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('Form Renderer Component form rule ordering', () => {
|
||||
let fixture: ComponentFixture<FormRendererComponent<any>>;
|
||||
let formService: FormService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormRendererComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: FORM_RULES_MANAGER,
|
||||
useFactory: (service: FormService) => new HideOnFieldValueChangedRulesManager(service),
|
||||
deps: [FormService]
|
||||
}
|
||||
]
|
||||
});
|
||||
fixture = TestBed.createComponent(FormRendererComponent<any>);
|
||||
formService = TestBed.inject(FormService);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should revalidate after a real rules manager hides an empty required field', () => {
|
||||
const form = new FormModel(
|
||||
{
|
||||
id: 'rule-ordering-form',
|
||||
fields: [
|
||||
{
|
||||
id: 'container',
|
||||
type: 'container',
|
||||
numberOfColumns: 1,
|
||||
fields: { 1: [{ id: 'requiredText', type: 'text', name: 'Required text', required: true }] }
|
||||
}
|
||||
]
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
formService
|
||||
);
|
||||
|
||||
fixture.componentInstance.formDefinition = form;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.isValid).toBe(false, 'form should start invalid with a visible empty required field');
|
||||
|
||||
formService.formRulesEvent.next({ type: 'fieldValueChanged', form } as any);
|
||||
|
||||
expect(form.isValid).toBe(true, 'the rule-driven hide must be reflected in form validity');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -156,10 +156,15 @@ export class FormRendererComponent<T> implements OnInit, OnDestroy {
|
||||
|
||||
this.formService.formRulesEvent
|
||||
.pipe(
|
||||
filter((event) => event?.type === 'fieldValueChanged' && event.form?.id === this.formDefinition?.id),
|
||||
filter((event) => !!event?.type && event.form?.id === this.formDefinition?.id),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
)
|
||||
.subscribe(() => this.visibilityService.refreshVisibility(this.formDefinition));
|
||||
.subscribe((event) => {
|
||||
if (event.type === 'fieldValueChanged') {
|
||||
this.visibilityService.refreshVisibility(this.formDefinition);
|
||||
}
|
||||
this.formDefinition?.validateForm();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -106,6 +106,14 @@
|
||||
<mat-icon adf-icon="zoom_out" />
|
||||
</button>
|
||||
|
||||
<button id="viewer-rotate-page-button"
|
||||
title="{{ 'ADF_VIEWER.ARIA.ROTATE' | translate }}"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.ROTATE' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="rotatePage()">
|
||||
<mat-icon adf-icon="rotate_left" />
|
||||
</button>
|
||||
|
||||
<button id="viewer-scale-page-button"
|
||||
role="button" aria-pressed="true"
|
||||
title="{{ 'ADF_VIEWER.ARIA.FIT_PAGE' | translate }}"
|
||||
@@ -114,6 +122,5 @@
|
||||
(click)="pageFit()">
|
||||
<mat-icon adf-icon="zoom_out_map" />
|
||||
</button>
|
||||
|
||||
</adf-toolbar>
|
||||
</div>
|
||||
|
||||
@@ -461,8 +461,10 @@ describe('Test PdfViewer - User interaction', () => {
|
||||
let component: PdfViewerComponent;
|
||||
let testingUtils: UnitTestingUtils;
|
||||
let pdfViewerSpy: jasmine.Spy;
|
||||
let pageViewMock: { width: number; height: number; scale: number; rotation: number; update: jasmine.Spy };
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
pageViewMock = { width: 100, height: 100, scale: 1, rotation: 0, update: jasmine.createSpy() };
|
||||
pdfViewerSpy = jasmine.createSpy('PDFViewer').and.returnValue({
|
||||
setDocument: jasmine.createSpy().and.returnValue({
|
||||
loadingTask: () => ({
|
||||
@@ -479,7 +481,7 @@ describe('Test PdfViewer - User interaction', () => {
|
||||
update: jasmine.createSpy(),
|
||||
currentScaleValue: 1,
|
||||
_currentPageNumber: 1,
|
||||
_pages: [{ width: 100, height: 100, scale: 1 }]
|
||||
_pages: [pageViewMock]
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@@ -617,6 +619,30 @@ describe('Test PdfViewer - User interaction', () => {
|
||||
}), 300);
|
||||
});
|
||||
|
||||
describe('Rotation', () => {
|
||||
it('should rotate only the current page counter-clockwise by 90 degrees', () => {
|
||||
testingUtils.clickByCSS('#viewer-rotate-page-button');
|
||||
|
||||
expect(pageViewMock.update).toHaveBeenCalledWith({ rotation: 270 });
|
||||
});
|
||||
|
||||
it('should wrap rotation back to 0 after a full turn', () => {
|
||||
pageViewMock.rotation = 90;
|
||||
|
||||
testingUtils.clickByCSS('#viewer-rotate-page-button');
|
||||
|
||||
expect(pageViewMock.update).toHaveBeenCalledWith({ rotation: 0 });
|
||||
});
|
||||
|
||||
it('should recompute the document overflow after rotation', () => {
|
||||
spyOn(component, 'setDocumentOverflow');
|
||||
|
||||
testingUtils.clickByCSS('#viewer-rotate-page-button');
|
||||
|
||||
expect(component.setDocumentOverflow).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resize interaction', () => {
|
||||
it('should resize event trigger setScaleUpdatePages', () => {
|
||||
spyOn(component, 'onResize');
|
||||
|
||||
@@ -577,6 +577,24 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
this.setScaleUpdatePages(newScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the currently displayed page 90 degrees counter-clockwise.
|
||||
*
|
||||
* Only the current page is affected; every other page keeps its own rotation.
|
||||
* The angle wraps around, so four consecutive calls return the page to its
|
||||
* original orientation (0 → 270 → 180 → 90 → 0). The document overflow state
|
||||
* is recomputed afterwards, since rotation swaps the page width and height.
|
||||
*/
|
||||
rotatePage() {
|
||||
if (this.pdfViewer) {
|
||||
const pageView = this.pdfViewer._pages[this.pdfViewer._currentPageNumber - 1];
|
||||
if (pageView) {
|
||||
pageView.update({ rotation: (pageView.rotation + 270) % 360 });
|
||||
this.setDocumentOverflow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load the previous page
|
||||
*/
|
||||
|
||||
+1
-1
@@ -131,7 +131,7 @@
|
||||
"react-dom": "19.2.6",
|
||||
"rimraf": "6.1.3",
|
||||
"sass-loader": "16.0.8",
|
||||
"semver": "7.6.3",
|
||||
"semver": "7.8.5",
|
||||
"sinon": "22.1.0",
|
||||
"spdx-license-list": "6.11.0",
|
||||
"storybook": "10.4.0",
|
||||
|
||||
Generated
+33
-32
@@ -25,6 +25,7 @@ overrides:
|
||||
webpack-dev-server: '>=6.0.0'
|
||||
uuid: '>=11.1.1'
|
||||
undici: 8.9.0
|
||||
'@xmldom/xmldom@<0.8.15': 0.8.15
|
||||
|
||||
importers:
|
||||
|
||||
@@ -302,8 +303,8 @@ importers:
|
||||
specifier: 16.0.8
|
||||
version: 16.0.8(@rspack/core@1.6.8)(sass-embedded@1.100.0)(sass@1.101.0)(webpack@5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25))
|
||||
semver:
|
||||
specifier: 7.6.3
|
||||
version: 7.6.3
|
||||
specifier: 7.8.5
|
||||
version: 7.8.5
|
||||
sinon:
|
||||
specifier: 22.1.0
|
||||
version: 22.1.0
|
||||
@@ -4595,10 +4596,9 @@ packages:
|
||||
resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@xmldom/xmldom@0.8.13':
|
||||
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
|
||||
'@xmldom/xmldom@0.8.15':
|
||||
resolution: {integrity: sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
deprecated: this version has critical issues, please update to the latest version
|
||||
|
||||
'@xtuc/ieee754@1.2.0':
|
||||
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
|
||||
@@ -9698,7 +9698,7 @@ snapshots:
|
||||
chokidar: 4.0.3
|
||||
convert-source-map: 1.9.0
|
||||
reflect-metadata: 0.2.2
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
yargs: 18.0.0
|
||||
optionalDependencies:
|
||||
@@ -12524,7 +12524,7 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
picomatch: 4.0.5
|
||||
rxjs: 7.8.2
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
webpack-merge: 5.10.0
|
||||
optionalDependencies:
|
||||
@@ -12581,7 +12581,7 @@ snapshots:
|
||||
'@nx/js': 23.1.1(@babel/traverse@7.29.7(supports-color@7.2.0))(nx@23.1.1)(supports-color@7.2.0)
|
||||
'@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3)
|
||||
detect-port: 2.1.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tree-kill: 1.2.2
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
@@ -12603,7 +12603,7 @@ snapshots:
|
||||
enquirer: 2.3.6
|
||||
minimatch: 10.2.6
|
||||
nx: 23.1.1
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
@@ -12618,7 +12618,7 @@ snapshots:
|
||||
globals: 17.6.0
|
||||
jsonc-eslint-parser: 2.4.2
|
||||
picocolors: 1.1.1
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.70.0(eslint@10.8.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)
|
||||
@@ -12639,7 +12639,7 @@ snapshots:
|
||||
'@nx/devkit': 23.1.1(nx@23.1.1)
|
||||
'@nx/js': 23.1.1(@babel/traverse@7.29.7(supports-color@7.2.0))(nx@23.1.1)(supports-color@7.2.0)
|
||||
eslint: 10.8.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
typescript: 6.0.3
|
||||
optionalDependencies:
|
||||
@@ -12680,7 +12680,7 @@ snapshots:
|
||||
npm-run-path: 4.0.1
|
||||
picocolors: 1.1.1
|
||||
picomatch: 4.0.5
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
source-map-support: 0.5.19
|
||||
tinyglobby: 0.2.17
|
||||
tslib: 2.8.1
|
||||
@@ -12789,7 +12789,7 @@ snapshots:
|
||||
sass: 1.101.0
|
||||
sass-embedded: 1.100.0
|
||||
sass-loader: 16.0.8(@rspack/core@1.6.8)(sass-embedded@1.100.0)(sass@1.101.0)(webpack@5.105.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25))
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
source-map-loader: 5.0.0(webpack@5.105.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25))
|
||||
style-loader: 3.3.4(webpack@5.105.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25))
|
||||
ts-checker-rspack-plugin: 1.4.0(@rspack/core@1.6.8)(tslib@2.8.1)(typescript@5.9.3)
|
||||
@@ -12842,7 +12842,7 @@ snapshots:
|
||||
'@nx/eslint': 23.1.1(@babel/traverse@7.29.7(supports-color@7.2.0))(@zkochan/js-yaml@0.0.7)(eslint@10.8.0(jiti@2.7.0)(supports-color@7.2.0))(nx@23.1.1)(supports-color@7.2.0)
|
||||
'@nx/js': 23.1.1(@babel/traverse@7.29.7(supports-color@7.2.0))(nx@23.1.1)(supports-color@7.2.0)
|
||||
'@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3)
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
storybook: 10.4.0(@testing-library/dom@10.4.1)(prettier@3.9.6)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
@@ -12954,7 +12954,7 @@ snapshots:
|
||||
enquirer: 2.3.6
|
||||
nx: 23.1.1
|
||||
picomatch: 4.0.5
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tslib: 2.8.1
|
||||
yargs-parser: 21.1.1
|
||||
transitivePeerDependencies:
|
||||
@@ -14127,7 +14127,7 @@ snapshots:
|
||||
debug: 4.4.3(supports-color@7.2.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tsutils: 3.21.0(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
@@ -14189,7 +14189,7 @@ snapshots:
|
||||
'@typescript-eslint/typescript-estree': 5.62.0(supports-color@7.2.0)(typescript@5.9.3)
|
||||
eslint: 10.8.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@@ -14373,7 +14373,7 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@xmldom/xmldom@0.8.13': {}
|
||||
'@xmldom/xmldom@0.8.15': {}
|
||||
|
||||
'@xtuc/ieee754@1.2.0': {}
|
||||
|
||||
@@ -15189,7 +15189,7 @@ snapshots:
|
||||
postcss-modules-scope: 3.2.1(postcss@8.5.25)
|
||||
postcss-modules-values: 4.0.0(postcss@8.5.25)
|
||||
postcss-value-parser: 4.2.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.105.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -15203,7 +15203,7 @@ snapshots:
|
||||
postcss-modules-scope: 3.2.1(postcss@8.5.25)
|
||||
postcss-modules-values: 4.0.0(postcss@8.5.25)
|
||||
postcss-value-parser: 4.2.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -15217,7 +15217,7 @@ snapshots:
|
||||
postcss-modules-scope: 3.2.1(postcss@8.5.25)
|
||||
postcss-modules-values: 4.0.0(postcss@8.5.25)
|
||||
postcss-value-parser: 4.2.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.105.0(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -15231,7 +15231,7 @@ snapshots:
|
||||
postcss-modules-scope: 3.2.1(postcss@8.5.25)
|
||||
postcss-modules-values: 4.0.0(postcss@8.5.25)
|
||||
postcss-value-parser: 4.2.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -16155,7 +16155,7 @@ snapshots:
|
||||
minimatch: 3.1.5
|
||||
node-abort-controller: 3.1.1
|
||||
schema-utils: 3.3.0
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
tapable: 2.3.3
|
||||
typescript: 5.9.3
|
||||
webpack: 5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -16703,7 +16703,7 @@ snapshots:
|
||||
'@babel/parser': 7.29.7
|
||||
'@istanbuljs/schema': 0.1.6
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -16732,7 +16732,7 @@ snapshots:
|
||||
|
||||
jasmine-reporters@2.5.2:
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.8.13
|
||||
'@xmldom/xmldom': 0.8.15
|
||||
mkdirp: 1.0.4
|
||||
|
||||
jest-regex-util@30.4.0: {}
|
||||
@@ -16796,7 +16796,7 @@ snapshots:
|
||||
acorn: 8.17.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
espree: 9.6.1
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
|
||||
jsonc-parser@3.3.1: {}
|
||||
|
||||
@@ -17116,7 +17116,7 @@ snapshots:
|
||||
|
||||
make-dir@4.0.0:
|
||||
dependencies:
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
|
||||
make-dir@5.1.0:
|
||||
optional: true
|
||||
@@ -17859,7 +17859,7 @@ snapshots:
|
||||
cosmiconfig: 9.0.2(typescript@5.9.3)
|
||||
jiti: 1.21.7
|
||||
postcss: 8.5.25
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.105.0(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -17871,7 +17871,7 @@ snapshots:
|
||||
cosmiconfig: 9.0.2(typescript@5.9.3)
|
||||
jiti: 2.7.0
|
||||
postcss: 8.5.25
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.105.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -17883,7 +17883,7 @@ snapshots:
|
||||
cosmiconfig: 9.0.2(typescript@5.9.3)
|
||||
jiti: 2.7.0
|
||||
postcss: 8.5.25
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@rspack/core': 1.6.8
|
||||
webpack: 5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
@@ -18647,7 +18647,8 @@ snapshots:
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.6.3: {}
|
||||
semver@7.6.3:
|
||||
optional: true
|
||||
|
||||
semver@7.7.2: {}
|
||||
|
||||
@@ -19282,7 +19283,7 @@ snapshots:
|
||||
chalk: 4.1.2
|
||||
enhanced-resolve: 5.24.5
|
||||
micromatch: 4.0.8
|
||||
semver: 7.6.3
|
||||
semver: 7.8.5
|
||||
source-map: 0.7.6
|
||||
typescript: 5.9.3
|
||||
webpack: 5.109.2(clean-css@5.3.3)(cssnano@7.1.9(postcss@8.5.25))(csso@5.0.5)(esbuild@0.25.9)(html-minifier-terser@6.1.0)(postcss@8.5.25)
|
||||
|
||||
@@ -27,6 +27,7 @@ overrides:
|
||||
webpack-dev-server: '>=6.0.0'
|
||||
uuid: '>=11.1.1'
|
||||
undici: 8.9.0
|
||||
'@xmldom/xmldom@<0.8.15': 0.8.15
|
||||
|
||||
allowBuilds:
|
||||
'@parcel/watcher': true
|
||||
|
||||
@@ -34,6 +34,26 @@ error_out() {
|
||||
# bg: 40 black, 41 red, 44 blue, 45 purple
|
||||
}
|
||||
|
||||
# Retry to absorb npm registry propagation delay right after publish.
|
||||
NPM_RETRIES=24
|
||||
NPM_RETRY_DELAY=20
|
||||
|
||||
npm_pack_version() {
|
||||
local spec=$1
|
||||
local attempt
|
||||
for attempt in $(seq 1 "$NPM_RETRIES"); do
|
||||
echo "📦 npm pack attempt $attempt/$NPM_RETRIES: $spec"
|
||||
if npm pack "$spec"; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$attempt" -lt "$NPM_RETRIES" ]; then
|
||||
echo "⏳ Attempt $attempt/$NPM_RETRIES failed, retrying in ${NPM_RETRY_DELAY}s..."
|
||||
sleep "$NPM_RETRY_DELAY"
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
rm -rf temp
|
||||
mkdir temp
|
||||
cd temp
|
||||
@@ -42,12 +62,14 @@ for PACKAGE in ${projects[@]}
|
||||
do
|
||||
mkdir $PACKAGE
|
||||
cd $PACKAGE
|
||||
PKG_VERSION=""
|
||||
|
||||
# Handle js-api differently - increase major version by 1
|
||||
if [ $PACKAGE == 'js-api' ]; then
|
||||
if [ $VERSION == 'alpha' ] || [ $VERSION == 'beta' ] || [ $VERSION == 'latest' ]; then
|
||||
# For tag versions, we need to get the current version and increment
|
||||
CURRENT_VERSION=$(npm view @alfresco/$PACKAGE@$VERSION version)
|
||||
FALLBACK_VERSION=$CURRENT_VERSION
|
||||
MAJOR_VERSION=$(echo $CURRENT_VERSION | cut -d'.' -f1)
|
||||
NEXT_MAJOR=$((MAJOR_VERSION + 1))
|
||||
# Keep the rest of the version string
|
||||
@@ -55,6 +77,7 @@ do
|
||||
PACKAGE_VERSION="${NEXT_MAJOR}.${REST_VERSION}"
|
||||
else
|
||||
# For specific versions, just increment the major number
|
||||
FALLBACK_VERSION=$VERSION
|
||||
MAJOR_VERSION=$(echo $VERSION | cut -d'.' -f1)
|
||||
NEXT_MAJOR=$((MAJOR_VERSION + 1))
|
||||
REST_VERSION=$(echo $VERSION | cut -d'.' -f2-)
|
||||
@@ -65,13 +88,17 @@ do
|
||||
fi
|
||||
|
||||
# Try the calculated package version first
|
||||
PKG_VERSION=$(npm view @alfresco/$PACKAGE@$PACKAGE_VERSION version 2>/dev/null)
|
||||
|
||||
# If that fails for js-api, try the original version
|
||||
if [ -z "$PKG_VERSION" ] && [ $PACKAGE == 'js-api' ]; then
|
||||
echo "Warning: js-api@$PACKAGE_VERSION not found, trying @$VERSION"
|
||||
PACKAGE_VERSION=$VERSION
|
||||
PKG_VERSION=$(npm view @alfresco/$PACKAGE@$PACKAGE_VERSION version 2>/dev/null)
|
||||
if npm_pack_version '@alfresco/'$PACKAGE@$PACKAGE_VERSION; then
|
||||
PKG_VERSION=$PACKAGE_VERSION
|
||||
elif [ $PACKAGE == 'js-api' ]; then
|
||||
# If that fails, try the original (already resolved) version.
|
||||
# FALLBACK_VERSION, not $VERSION directly, since npm pack writes the
|
||||
# tarball under the resolved version, not the tag it was requested with.
|
||||
echo "Warning: js-api@$PACKAGE_VERSION not found, trying @$FALLBACK_VERSION"
|
||||
PACKAGE_VERSION=$FALLBACK_VERSION
|
||||
if [ -n "$PACKAGE_VERSION" ] && npm_pack_version '@alfresco/'$PACKAGE@$PACKAGE_VERSION; then
|
||||
PKG_VERSION=$PACKAGE_VERSION
|
||||
fi
|
||||
fi
|
||||
|
||||
# If still no version found, exit with error
|
||||
@@ -82,7 +109,6 @@ do
|
||||
|
||||
echo "Inspecting: $PACKAGE@$PKG_VERSION"
|
||||
|
||||
npm pack '@alfresco/'$PACKAGE@$PACKAGE_VERSION
|
||||
tar zxf 'alfresco-'$PACKAGE-$PKG_VERSION.tgz
|
||||
|
||||
if [ $PACKAGE == 'js-api' ]; then
|
||||
|
||||
Reference in New Issue
Block a user