Skip to content

Commit 64d64a2

Browse files
committed
Merge branch 'release/26.17.0'
2 parents 69746ae + 20ba34c commit 64d64a2

383 files changed

Lines changed: 4920 additions & 5821 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.
44

5+
26.17.0 (2026-08-31)
6+
====================
7+
8+
* PB&S 26-15 Release
9+
510
26.16.1 (2026-08-11)
611
====================
712

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright 2025 Center for Open Science
189+
Copyright 2026 Center for Open Science
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

docs/arch.md

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,97 @@
11
# 📂 Folder Structure
22

3-
Project based on principle **Feature-based Architecture**, this approach provides reusable and consistant
4-
features across the application.
3+
Project based on principle **Feature-based Architecture**: each feature owns its UI, routes, models, mappers, services, and store when needed. Shared and core code live outside features.
54

65
```bash
76
📦 src/
8-
├── 📂 features/ # Каталог із функціональними модулями
9-
│ ├── 📂 feature-name/
10-
│ │ ├── 📂 feature.component.ts/html/scss # Component with template and styles, and base logic file
11-
│ │ ├── 📂 feature-service.ts # Service or Facade to provide data for NGXS
12-
│ │ ├── 📂 feature.store.ts # NGXS Store for feature
13-
│ │ ├── 📂 feature.entitity.ts # Feature Interface for data, Types, Enums
14-
│ │ ├── 📂 feature.guards.ts # Guard's for feature routing and permissions
15-
│ │ ├── 📂 feature.resolvers.ts # Resolvers for data fetching and preloading
16-
│ │ ├── 📂 feature.utils.ts # Additinal utils for feature (formBuilders, converters, mappers)
17-
│ │ ├── feature.module.ts # Optional, if standalone
18-
│ │ ├── feature.routing.ts # Export of standalone components by path
7+
├── 📂 app/
8+
│ ├── 📂 features/ # Feature modules
9+
│ │ └── 📂 feature-name/
10+
│ │ ├── 📂 components/ # Feature UI pieces
11+
│ │ ├── 📂 pages/ # Route-level pages (when used)
12+
│ │ ├── 📂 models/ # Feature-local *.model.ts types
13+
│ │ ├── 📂 mappers/ # Feature API ↔ domain mappers
14+
│ │ ├── 📂 services/ # Feature HTTP / facade services
15+
│ │ ├── 📂 store/ # Feature NGXS state (actions, model, state, selectors)
16+
│ │ ├── 📂 enums/ # Feature enums
17+
│ │ ├── 📂 constants/ # Feature constants
18+
│ │ ├── feature.routes.ts # Feature routes
19+
│ │ └── feature.component.ts # Feature shell / entry component
20+
│ │
21+
│ ├── 📂 core/ # App-wide infrastructure
22+
│ │ ├── 📂 components/ # Shell UI (header, banners, …)
23+
│ │ ├── 📂 services/ # Global services
24+
│ │ ├── 📂 store/ # Core NGXS state (user, emails, …)
25+
│ │ ├── 📂 models/ # Core config / routing types
26+
│ │ ├── 📂 guards/
27+
│ │ ├── 📂 interceptors/
28+
│ │ ├── 📂 helpers/
29+
│ │ └── 📂 provider/
30+
│ │
31+
│ ├── 📂 shared/ # Cross-feature reusable code
32+
│ │ ├── 📂 components/ # Shared UI
33+
│ │ ├── 📂 directives/
34+
│ │ ├── 📂 pipes/
35+
│ │ ├── 📂 services/ # Shared HTTP / helpers
36+
│ │ ├── 📂 stores/ # Shared NGXS domains
37+
│ │ ├── 📂 models/ # Shared domain + JSON:API models
38+
│ │ ├── 📂 mappers/ # Shared mappers
39+
│ │ ├── 📂 enums/
40+
│ │ ├── 📂 guards/
41+
│ │ └── 📂 helpers/
42+
│ │
43+
│ ├── app.component.ts
44+
│ ├── app.config.ts
45+
│ ├── app.config.server.ts # SSR app config
46+
│ ├── app.routes.ts
47+
│ └── app.routes.server.ts # SSR routes
1948
20-
├── 📂 core/ # Base module for global services, components, and state
21-
│ ├── 📂 services/ # Global services (API, Auth, LocalStorage)
22-
│ ├── 📂 components/ # Global components (Header, Footer, Sidebar)
23-
│ ├── 📂 store/ # Core state management (Auth, Settings, Router)
24-
│ ├── core.module.ts # Optional, but must have a provider for core.
25-
26-
├── 📂 shared/ # Shared module for common components, directives, pipes, and services
27-
│ ├── 📂 ui/ # Shared UI components (Button, Input, Modal), or wrappers for 3rd party
28-
│ ├── 📂 directives/ # Shared Directives (ClickOutside, Draggable)
29-
│ ├── 📂 pipes/ # Shared Pipes (Filter, Sort, Format)
30-
│ ├── 📂 services/ # Services, Facades for shared logic (Http, LocalStorage)
31-
│ ├── 📂 store/ # Shared State management (Settings, Theme, Language)
32-
33-
├── app.routes.ts # General Entry point for routing
34-
├── main.ts # Providers Setup and Bootstrap
35-
├── package.json # Dependencies and Scripts
49+
├── 📂 assets/
50+
├── 📂 environments/
51+
├── 📂 styles/
52+
├── 📂 testing/ # Test helpers, mocks, builders (@testing/*)
53+
├── main.ts # Browser bootstrap
54+
├── main.server.ts # SSR bootstrap
55+
├── server.ts # Express / SSR server entry
56+
└── index.html
57+
```
58+
59+
---
3660

61+
## 📋 Models and mappers
62+
63+
- Types live in `*.model.ts` files (interfaces/types, not classes).
64+
- Shared catalog: `shared/models/<domain>/` with optional `*-json-api.model.ts` twins.
65+
- Feature-local types: `features/<feature>/models/`.
66+
- Mappers convert JSON:API ↔ domain at the service boundary.
67+
68+
See [Models Conventions](./models.md).
69+
70+
---
71+
72+
## 🗃️ State
73+
74+
- Shared domains: `shared/stores/<domain>/`
75+
- Feature domains: `features/<feature>/store/`
76+
- Core domains: `core/store/`
77+
78+
See [NGXS State Management](./ngxs.md).
3779

3880
---
39-
```
4081

4182
## 🚀 Dynamic File Generation (Schematics)
4283

43-
Use Angular CLI to generate new feature components, services, and modules.
84+
Use Angular CLI for scaffolding:
4485

4586
```sh
4687
ng generate component feature-name/components/new-component
88+
```
4789

4890
### 📌 Other Schematics:
4991

50-
| **Entity** | **Command** |
51-
|--------------|----------------------------------------------|
52-
| 📌 **Service** | `ng g s feature-name/services/new-service` |
53-
| 📦 **Module** | `ng g m feature-name` |
54-
| 🔐 **Guard** | `ng g g feature-name/guards/auth-guard` |
55-
| 🔄 **Pipe** | `ng g p shared/pipes/currency-format` |
56-
|**Directive** | `ng g d shared/directives/highlight` |
57-
58-
59-
```
92+
| **Entity** | **Command** |
93+
| ---------------- | ------------------------------------------ |
94+
| 📌 **Service** | `ng g s feature-name/services/new-service` |
95+
| 🔐 **Guard** | `ng g g feature-name/guards/auth-guard` |
96+
| 🔄 **Pipe** | `ng g p shared/pipes/currency-format` |
97+
|**Directive** | `ng g d shared/directives/highlight` |

docs/models.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Models Conventions
2+
3+
## Purpose
4+
5+
Models are TypeScript interfaces and types that describe data shapes. They are not classes. Mapping from API wire format to app domain happens in mappers.
6+
7+
## Layers
8+
9+
```
10+
JSON:API (*-json-api.model.ts) → mapper → domain model → store (*StateModel) → UI
11+
```
12+
13+
| Layer | Role | Naming |
14+
| -------- | ------------------------------------------------------ | ----------------------------------------------------- |
15+
| JSON:API | Wire/DTO shapes from the backend (`snake_case` fields) | `*JsonApi`, `*DataJsonApi`, `*ResponseJsonApi` |
16+
| Domain | App-facing shapes (`camelCase` fields) | Prefer descriptive names; `*Model` suffix is optional |
17+
| State | NGXS slice shape | Always `*StateModel` |
18+
| Form | Reactive form value shapes | `*Form`, `*FormGroup` |
19+
20+
Keep JSON:API types in services and mappers. Prefer domain models in stores, selectors, and components.
21+
22+
## Locations
23+
24+
| Location | Use for |
25+
| ---------------------------------------- | ------------------------------------------------------------------------------------------- |
26+
| `src/app/shared/models/<domain>/` | Cross-feature domain + JSON:API types |
27+
| `src/app/shared/models/common/json-api/` | Shared JSON:API primitives (`JsonApiResource`, `ItemResponse`, `ListResponse`, links, meta) |
28+
| `src/app/features/<feature>/models/` | Feature-local UI, form, and API types |
29+
| `src/app/**/store*/**/*.model.ts` | Colocated NGXS state models |
30+
| `src/app/core/models/` | App-wide config and core types |
31+
32+
## File naming
33+
34+
- Domain / general: `kebab-case.model.ts`
35+
- JSON:API twin: `kebab-case-json-api.model.ts` (dash, not dot)
36+
- Forms: `kebab-case-form.model.ts`
37+
- One concern per file when practical; group related exports in the same domain folder
38+
39+
Examples:
40+
41+
- `user.model.ts` + `user-json-api.model.ts`
42+
- `configured-addon.model.ts` + `configured-addon-json-api.model.ts`
43+
44+
## Symbol naming
45+
46+
- Domain: `UserModel`, `InstitutionUser`, `RegistrationCard` (suffix `Model` is encouraged for primary entities, not required for every interface)
47+
- JSON:API: always end with `JsonApi` (e.g. `UserDataJsonApi`, `UserResponseJsonApi`)
48+
- State: always `*StateModel` (e.g. `AddonsStateModel`, `SubjectsStateModel`)
49+
- Do not put snake_case field names on domain models; map them in the mapper
50+
51+
## Mappers
52+
53+
- Live in `shared/mappers/` or `features/<feature>/mappers/`
54+
- Convert `*JsonApi` → domain models (and domain → request payloads when needed)
55+
- Prefer static mapper classes (`UserMapper.fromUserGetResponse`) or focused `mapX` functions; keep one style within a feature
56+
57+
## Imports
58+
59+
Prefer the `@osf/` alias for app code:
60+
61+
```ts
62+
import { UserModel } from '@osf/shared/models/user/user.model';
63+
import { UserDataJsonApi } from '@osf/shared/models/user/user-json-api.model';
64+
```
65+
66+
Feature barrels are optional. When a feature has `models/index.ts`, import from the barrel:
67+
68+
```ts
69+
import { ModeratorModel } from '@osf/features/moderation/models';
70+
```
71+
72+
Otherwise import the file path directly. Do not mix `@osf/shared/models/...` and `@shared/models/...` in new code — use `@osf/...`.
73+
74+
## State models
75+
76+
Colocate with the store. Compose domain types inside `AsyncStateModel` / `AsyncStateWithTotalCount`:
77+
78+
```ts
79+
export interface FilesStateModel {
80+
files: AsyncStateModel<FileModel[]>;
81+
}
82+
```
83+
84+
See [NGXS docs](./ngxs.md) for store layout and async state shape.
85+
86+
## Checklist for new models
87+
88+
1. Put shared types under `shared/models/<domain>/`, feature-only types under `features/<feature>/models/`
89+
2. Add a `*-json-api.model.ts` twin only when typing an HTTP payload/response
90+
3. Use interfaces/types, not classes
91+
4. Keep domain fields camelCase; leave snake_case in JSON:API types
92+
5. Map at the service/mapper boundary before storing or rendering
93+
6. Name store interfaces `*StateModel`
94+
7. Import via `@osf/...`

docs/ngxs.md

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ The goal of using NGXS is to centralize and streamline the handling of applicati
3434

3535
### Diagram
3636

37-
[![OSF NGRX Diagram](./assets/osf-ngxs-diagram.png)](./assets/osf-ngxs-diagram.png)
37+
[![OSF NGXS Diagram](./assets/osf-ngxs-diagram.png)](./assets/osf-ngxs-diagram.png)
3838

3939
---
4040

@@ -45,44 +45,51 @@ Typical NGXS-related files are organized as follows:
4545
```
4646
src/app/shared/stores/
4747
└── addons/
48-
├── addons.actions.ts # All action definitions
49-
├── addons.model.ts # Interfaces & data model
50-
├── addons.state.ts # State implementation
51-
├── addons.selectors.ts # Reusable selectors
48+
├── addons.actions.ts # Action definitions
49+
├── addons.model.ts # State interface (*StateModel) and defaults
50+
├── addons.state.ts # State implementation
51+
├── addons.selectors.ts # Selectors
5252
```
5353

5454
```
5555
src/app/shared/services/
5656
└── addons/
57-
├── addons.service.ts # External API calls
57+
├── addons.service.ts # External API calls (map JSON:API → domain)
5858
```
5959

60+
Feature stores follow the same file set under `features/<feature>/store/`. Core stores live under `core/store/`.
61+
6062
---
6163

6264
## State Models
6365

64-
The OSF Angular project follows a consistent NGXS state model structure to ensure clarity, predictability, and alignment across all features. The recommended shape for each domain-specific state is as follows:
66+
State interfaces are named `*StateModel` and live in the colocated `*.model.ts` file. They are TypeScript interfaces (not classes). Domain entity types come from `shared/models` or feature `models/` — see [Models Conventions](./models.md).
6567

66-
1. Domain state pattern:
68+
Use `AsyncStateModel<T>` (and `AsyncStateWithTotalCount` when a total count is needed) from `shared/models/store/`:
6769

6870
```ts
69-
domain: {
70-
data: [], // Array of typed model data (e.g., Project[], User[])
71-
isLoading: false, // Indicates if data retrieval (GET) is in progress
72-
isSubmitting: false, // Indicates if data submission (POST/PUT/DELETE) is in progress
73-
error: null, // Captures error messages from failed HTTP requests
71+
export interface AsyncStateModel<T> {
72+
data: T;
73+
isLoading: boolean;
74+
isSubmitting?: boolean;
75+
error: string | null;
7476
}
7577
```
7678

77-
2. `data` holds the strongly typed collection of entities defined by the feature's interface or model class.
78-
79-
3. `isLoading` is a signal used to inform the component and template layer that a read or fetch operation is currently pending.
79+
Example store shape:
8080

81-
4. `isSubmitting` signals that a write operation (form submission, update, delete, etc.) is currently in progress.
81+
```ts
82+
export interface FilesStateModel {
83+
files: AsyncStateModel<FileModel[]>;
84+
}
85+
```
8286

83-
5. `error` stores error state information (commonly strings or structured error objects) that result from failed service interactions. This can be displayed in UI or logged for debugging.
87+
1. `data` holds strongly typed domain data (not raw JSON:API payloads when a domain model exists).
88+
2. `isLoading` indicates a read/fetch is in progress.
89+
3. `isSubmitting` indicates a write (create/update/delete) is in progress.
90+
4. `error` stores a failed request message for UI or logging.
8491

85-
Each domain state should be minimal, normalized, and scoped to its specific feature, mirroring the structure and shape of the corresponding OSF backend API response.
92+
Each domain state should be minimal and scoped to its feature.
8693

8794
---
8895

@@ -96,12 +103,13 @@ Each domain state should be minimal, normalized, and scoped to its specific feat
96103

97104
## Testing
98105

99-
- [Testing Strategy](docs/testing.md)
100-
- [NGXS State Testing Strategy](docs/testing.md#ngxs-state-testing-strategy)
106+
- [Testing Strategy](./testing.md)
107+
- [NGXS State Testing Strategy](./testing.md#15-testing-ngxs-state)
101108

102109
---
103110

104111
## Documentation
105112

106-
Refer to the official NGXS documentation for full API details and advanced usage:
107-
[https://www.ngxs.io/docs](https://www.ngxs.io/docs)
113+
- [Models Conventions](./models.md)
114+
- [Folder Structure](./arch.md)
115+
- Official NGXS docs: [https://www.ngxs.io/docs](https://www.ngxs.io/docs)

eslint.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ module.exports = defineConfig(
4141
style: 'kebab-case',
4242
},
4343
],
44+
'@angular-eslint/prefer-signals': [
45+
'error',
46+
{
47+
preferQuerySignals: true,
48+
preferReadonlySignalProperties: false,
49+
},
50+
],
51+
'@angular-eslint/prefer-output-emitter-ref': 'error',
4452
'no-duplicate-imports': 'error',
4553
'simple-import-sort/imports': [
4654
'error',

0 commit comments

Comments
 (0)