Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e918f80
refactor(components): migrate to control flow and OnPush where safe
Oct 1, 2026
ca0675c
test: restore skipped specs, add shared fixtures, service/helper spec…
Oct 1, 2026
5ba5a48
build: add quality gates and generators
Oct 1, 2026
b8f0668
feat(tooling): configure sdk-config.json from environment and diagnos…
Oct 1, 2026
f5006e6
feat(release): changelog tooling in the established format and versio…
Oct 1, 2026
3391c7b
chore(deps): stay on Angular 21.2.25, Node 24 and TypeScript 5.9; dro…
Oct 1, 2026
c8d4cd8
docs: getting started, configuration, CI usage, customizing, testing …
Oct 1, 2026
f41596e
docs(agents): agent guide, custom agents, skills, prompts and consist…
Oct 1, 2026
c0311c7
test: move unit tests from Karma and Jasmine to Vitest
Oct 1, 2026
444dd23
chore(deps): update jsdom to 30; document the Vitest 4 constraint
Oct 1, 2026
b8f305f
test: replace the last two skipped specs with behaviour tests
Oct 1, 2026
974ffaf
chore: remove deprecated API usage and enforce it in lint
Oct 1, 2026
6700484
refactor: split stateless helpers out of AngularPConnectService
Oct 1, 2026
5924e27
test: add behaviour tests for field components
Oct 1, 2026
c034016
fix: correct field component bugs found by new behaviour tests
Oct 1, 2026
862581f
docs: track follow-ups after modernization in ADR 0003
Oct 1, 2026
4223f66
chore: remove CLAUDE.md; Claude Code falls back to AGENTS.md
Oct 1, 2026
a13352f
chore: keep new tooling out of package.json scripts
Oct 1, 2026
8817895
chore: remove the doctor script and its references
Oct 1, 2026
a570f46
chore: drop the strengthen-specs prompt and spec follow-up item
Oct 1, 2026
aaffe13
docs: replace the eight specialised agents with one sdk-engineer agent
Oct 1, 2026
4bc73b5
docs: make the sdk-engineer agent exhaustive
Oct 1, 2026
eaa632e
ci: update GitHub Actions to the latest major versions
Oct 1, 2026
d3beb6e
ci: run the commitlint job on the cheaper ubuntu-slim runner
Oct 1, 2026
29c3db5
Revert "ci: run the commitlint job on the cheaper ubuntu-slim runner"
Oct 1, 2026
e823be6
ci: run workflows on ubuntu-24.04-arm runners
Oct 1, 2026
d989d4d
docs: note the arm64 CI runners
Oct 1, 2026
0547ebc
docs: remove prompts and extend the sdk-engineer agent
Oct 1, 2026
7df6494
docs: drop the year from the agent practices heading
Oct 1, 2026
9312686
fix: track stable keys in the navbar page lists to avoid NG0100
Oct 1, 2026
56a4853
fix: hide the RootContainer missing message until the component name …
Oct 1, 2026
4db3c54
fix: always give ListView header cells a defined column id
Oct 1, 2026
68f8569
fix: apply DataReference store updates outside change detection
Oct 1, 2026
d6c04cc
fix: apply store updates after change detection in the bridge
Oct 1, 2026
25cf96a
refactor: drop the DataReference-specific deferral covered by the bridge
Oct 1, 2026
d26da44
fix: flag component views after store and data updates in zoneless apps
Oct 1, 2026
259b6eb
revert: drop the change-detection fixes for NG0100 for now
Oct 1, 2026
a3b0cca
docs: keep only the sdk-pconnect-api skill
Oct 1, 2026
532dced
docs: remove the newly added docs
Oct 1, 2026
0b1f05f
docs: add onboarding, theming and troubleshooting parts to the sdk-en…
Oct 1, 2026
17f6ee1
docs: restructure the sdk-engineer agent for retrieval
Oct 1, 2026
aa847f0
docs: drop the NG0100 details from the agent
Oct 1, 2026
594c0cb
docs: add design system integration to the sdk-engineer agent
Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1,891 changes: 1,891 additions & 0 deletions .github/agents/sdk-engineer.agent.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copilot instructions

Read `AGENTS.md` first: it has the project map, the verification loop, the definition of done, task recipes and the rules that must not be broken. Area-specific rules are in `.github/instructions/`.

Essentials:

- Verify with `node scripts/verify.js --quick` while iterating and `node scripts/verify.js` before you finish. Report anything you could not verify (Playwright E2E needs a Pega Infinity server).
- All data access goes through `pConn$`/PConnect; never call Infinity directly. Children render through `<component-mapper>`. Field components extend `FieldBase`.
- New components: `node scripts/new-component.js <kind> <kebab-name> <PegaName>` (registers in `public-api.ts` and the component map).
- Do not edit `dist/`, `packages/angular-sdk-overrides/lib` (generated), or `etc/*.api.md` by hand; use the generating scripts.
- Conventional commits (`feat:`, `fix:`, `chore:`, `docs:`).
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2
updates:
# Version updates run once a quarter and are grouped into a single PR per ecosystem to keep noise to a minimum.
# Dependabot security updates are controlled separately in the repository settings.
- package-ecosystem: npm
directory: /
schedule:
interval: quarterly
open-pull-requests-limit: 1
groups:
all-dependencies:
patterns: ['*']
- package-ecosystem: github-actions
directory: /
schedule:
interval: quarterly
open-pull-requests-limit: 1
groups:
all-actions:
patterns: ['*']
2 changes: 2 additions & 0 deletions .github/instructions/bridge.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ This directory is the SDK's integration layer that maps the PConnect component t
|------|---------------|
| `angular-pconnect.ts` | Injectable service that manages store subscriptions, component registration, prop comparison, action wiring, and form field lifecycle |
| `component-mapper/component-mapper.component.ts` | Dynamic component renderer — resolves component names to Angular component classes and creates them via `ViewContainerRef` |
| `helpers/pconnect-props.ts` | Stateless `resolveComponentProps()` — resolves config + additional props used for change detection |
| `helpers/pconnect-form-field.ts` | Stateless `removeFormFieldAndContextNode()` — form field and context tree cleanup on unsubscribe |
| `helpers/sdk_component_map.ts` | Singleton component registry — maps names to Angular component classes |
| `helpers/sdk-pega-component-map.ts` | Pega-provided component registry (master map of all SDK components) |

Expand Down
26 changes: 14 additions & 12 deletions .github/instructions/testing.instructions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
applyTo: "projects/angular-test-app/tests/**,**/*.spec.*,**/*.test.*"
description: "Use when writing or modifying tests. Covers Karma/Jasmine unit tests, Playwright E2E setup, test credentials, helpers, and configuration."
description: "Use when writing or modifying tests. Covers Vitest unit tests, Playwright E2E setup, test credentials, helpers, and configuration."
---
# Testing

This project uses Karma/Jasmine for unit tests and Playwright for end-to-end tests.
This project uses Vitest (via Angular's `unit-test` builder) for unit tests and Playwright for end-to-end tests.

## Structure

Expand Down Expand Up @@ -33,24 +33,26 @@ packages/angular-sdk-components/src/lib/
└── angular-pconnect.service.spec.ts # Bridge service unit test
```

## Unit Tests (Karma/Jasmine)
## Unit Tests (Vitest)

### Running
```bash
ng test angular-sdk-components # Run library unit tests
npx ng test angular-sdk-components --watch=false # Vitest on jsdom, no Pega server needed
npx ng test angular-sdk-components --watch=false --coverage # same + coverage report in coverage/ and a threshold floor
```

### Configuration
- Karma config: `packages/angular-sdk-components/tsconfig.spec.json`
- Framework: Jasmine with Karma runner
- Browser: Chrome (karma-chrome-launcher)
- Coverage: karma-coverage reporter
- Runner: `test` target in `angular.json` (`@angular/build:unit-test`, `runner: vitest`), `packages/angular-sdk-components/vitest.config.ts`; coverage thresholds (`coverageThresholds`) are a floor, raise them as coverage grows
- TypeScript: `packages/angular-sdk-components/tsconfig.spec.json`
- Harness: `src/test-setup.ts` (global `PCore` stand-in, `createMockPConn()`, `getA11yViolations()`) and `src/test-hooks.ts` (global hooks, a setup file) and `src/test-utils.ts` (`stubComponentMapper`, `getMappedComponents`).

### Writing Unit Tests
- Place spec files alongside the component: `component-name.component.spec.ts`
- Use `TestBed.configureTestingModule()` for component setup
- Use `ComponentFixture` for component interaction
- Mock `PCore` and `pConn$` — components always expect these runtime globals
- Standalone components go in `imports`, never `declarations`; do not use `waitForAsync` (the app is zoneless): use `async`/`await`
- Set `component.pConn$ = createMockPConn()`; override only what the test needs (`pConn.getConfigProps = () => ({...})`, `pConn.resolveConfigProps = p => p`). Field components also need `formGroup$ = new FormGroup({})`
- Do not import the component map in a spec at module level (circular import); load it lazily like `sdk_component_map.spec.ts`
- Use `getA11yViolations(el)` for accessibility assertions (see `field-a11y.spec.ts`)
- No specs are skipped; keep it that way. `vi.mock` of relative modules is unsupported by the Angular builder: provide `PCore` data instead

### Example
```typescript
Expand All @@ -68,7 +70,7 @@ describe('TextInputComponent', () => {
fixture = TestBed.createComponent(TextInputComponent);
component = fixture.componentInstance;
// Must mock pConn$ before detectChanges
component.pConn$ = mockPConnect;
(component as any).pConn$ = createMockPConn(); // from src/test-setup.ts
fixture.detectChanges();
});

Expand Down
60 changes: 60 additions & 0 deletions .github/skills/sdk-pconnect-api/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: sdk-pconnect-api
description: Discover and correctly use the PConnect and PCore APIs (version-locked typedefs) when implementing or debugging SDK components - search recipes, the handful of APIs used everywhere, and how to mock them in tests.
---

# PConnect / PCore API

The engine (`@pega/constellationjs`) exposes two globals the SDK builds on. The authoritative, version-locked API is in `node_modules/@pega/pcore-pconnect-typedefs/` - read it instead of guessing.

| Where | Contents |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `interpreter/c11n-env.d.ts` | `PConnect` (per-component API, class `C11nEnv`) |
| `actions/api.d.ts` | the object returned by `pConn$.getActionsApi()` |
| `pcore.d.ts` | `PCore` global: `getStore`, `getConstants`, `getDataApiUtils`, `getContainerUtils`, `getLocaleUtils`, `getPubSubUtils`, `getEnvironmentInfo`, `getContextTreeManager`, `getMessageManager`, `getCaseUtils`, `getFormUtils`, `getAttachmentUtils`, `getRuntimeParamsAPI`, `getActionsSequencer` ... |
| `constants.d.ts` | `PCore.getConstants()` shape (`CASE_INFO`, `PUB_SUB_EVENTS`, ...) |

## Search recipes

```bash
T=node_modules/@pega/pcore-pconnect-typedefs
grep -n "^\s*[a-zA-Z]*(.*):" $T/interpreter/c11n-env.d.ts | less # every PConnect method signature
grep -n "getLocalizedValue" -B12 $T/interpreter/c11n-env.d.ts # docs + @example above a method
grep -n "^\s*[a-zA-Z]*(.*)" $T/actions/api.d.ts # action API
grep -rn "getDataApiUtils" $T/pcore.d.ts
grep -rn "yourSymbol" $T --include=*.d.ts -l
```

Each method carries JSDoc with an `@example`; read it for argument shapes. Then confirm real usage in the repo: `grep -rn "methodName(" packages/angular-sdk-components/src/lib --include=*.ts`.

## APIs used by almost every component

| API | Purpose |
| ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| `pConn$.getConfigProps()` + `pConn$.resolveConfigProps(props)` | read the rule's config; **always resolve** (expressions like `@P .Name` become values) |
| `pConn$.getRawMetadata()` | unresolved metadata (`type`, `config`) - used to decide which child to render |
| `pConn$.getChildren()` -> each `.getPConnect()` | child PConnect nodes for `<component-mapper>` |
| `pConn$.getActionsApi()` | `updateFieldValue`, `triggerFieldChange` (used only through `handleEvent`), plus case/assignment actions |
| `pConn$.getStateProps()` | state binding, for example `getStateProps().value` -> `propName` |
| `pConn$.getInheritedProps()` / `setInheritedProp(k, v)` | props inherited from parent views (display mode, read-only) |
| `pConn$.clearErrorMessages({ property })` | clear validation messages when the user edits |
| `localizeText(pConn$, text, localePath?, ruleKey?)` (`_helpers/localization.ts`) | localization of literals; wraps `getLocalizationService().getLocalizedText` because `pConn$.getLocalizedValue` is deprecated |
| `pConn$.getComponentName()`, `getContextName()`, `getPageReference()`, `getValue(ref)`, `isEditable()` | identity, context and value access |
| `PCore.getConstants()` | enumerations (`CASE_INFO`, `PUB_SUB_EVENTS`...) |
| `PCore.getDataApiUtils().getData(view, params, context)` | data pages/views for widgets |
| `PCore.getPubSubUtils().subscribe/unsubscribe/publish` | engine events (always unsubscribe on destroy) |
| `PCore.getEnvironmentInfo()` | locale, time zone, operator info |

## Rules
- Do not use members the typedefs mark `@deprecated` (the lint rule `@typescript-eslint/no-deprecated` fails the build); read the replacement from the JSDoc.

- Components never call Infinity REST; use these APIs.
- Do not read field data from `PCore.getStore().getState()`; use config props (the bridge handles store subscription and re-render).
- Do not mutate objects returned by `getConfigProps()`/`getInheritedProps()`; copy first.
- Version drift: `compareSdkPCoreVersions()` in `_helpers/versionHelpers.ts` exists for behaviour that differs by engine version.

## Testing code that uses these APIs

- `createMockPConn()` (in `src/test-setup.ts`) returns empty values for known getters and no-op functions for others; override only what the test needs: `pConn.getConfigProps = () => ({ label: 'x' }); pConn.resolveConfigProps = p => p;`
- The global `PCore` is a lenient stub reset before each spec; set specific methods on `(globalThis as any).PCore` inside the test (`getStore` -> capture the listener to simulate store updates).
- Unknown `PCore.getXxx()` returns a shallow object: ALL_CAPS members resolve to their own name (constants), other members to no-ops.
6 changes: 3 additions & 3 deletions .github/workflows/commitlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ permissions:

jobs:
commitlint:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04-arm

strategy:
matrix:
node-version: [24.x]

steps:
# Full history is required so the PR base and head commits are both available.
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: npm
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Copilot Setup Steps

# Prepares the environment used by the GitHub Copilot coding agent so it can run `node scripts/verify.js` immediately.
on:
workflow_dispatch:
push:
paths: [.github/workflows/copilot-setup-steps.yml]
pull_request:
paths: [.github/workflows/copilot-setup-steps.yml]

jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24.x
cache: npm
- run: npm ci --ignore-scripts
- name: Sanity check
run: node scripts/verify.js --quick
6 changes: 3 additions & 3 deletions .github/workflows/install-build-sdk-pack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ permissions:
jobs:
build:

runs-on: ubuntu-latest
runs-on: ubuntu-24.04-arm

strategy:
matrix:
node-version: [24.x]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: npm
Expand Down
61 changes: 61 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: quality

on:
workflow_dispatch:
pull_request:
branches: [master, 'release/**']

permissions:
contents: read

concurrency:
group: quality-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24.x
cache: npm
- run: npm ci --ignore-scripts
- name: Lint and format
run: npm run lint
- name: Build library
run: npm run build-angular-sdk-components
- name: Public API report is up to date
run: npx api-extractor run
- name: CHANGELOG.md follows the project format
run: node scripts/changelog.js check
- name: Agent assets are consistent
run: node scripts/check-agent-assets.js
- name: Tooling script tests
run: node --test "scripts/__tests__/*.test.js"
- name: noImplicitAny ratchet
run: node scripts/check-implicit-any.js
- name: Build overrides package
run: npm run build-overrides
- name: Overrides package type-checks against the built library
run: npx ngc -p tsconfig.overrides-check.json
- name: Verify package tarballs
run: node scripts/smoke-pack.js

unit-tests:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24.x
cache: npm
- run: npm ci --ignore-scripts
- run: npx ng test angular-sdk-components --watch=false --coverage
- uses: actions/upload-artifact@v7
if: always()
with:
name: coverage
path: coverage/angular-sdk-components
retention-days: 7
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ packages/angular-sdk-overrides/SECURITY.md
/tests/playwright-report

**/tsconfig.*.tsbuildinfo
temp/

# local environment files
.env
.env.local
/coverage
3 changes: 0 additions & 3 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint -e "$1"
3 changes: 0 additions & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
**/*.md
**/*.svg
**/assets
etc/
temp/
coverage/
Loading
Loading