428d19f60f
CI / commits (pull_request) Successful in 3m34s
CI / scan (pull_request) Successful in 3m47s
CI / check (pull_request) Successful in 6m15s
CI / a11y (pull_request) Successful in 2m26s
Docs site / build (pull_request) Successful in 1m7s
CI / perf (pull_request) Successful in 4m33s
Lands the BFF-side skeleton for the apf-ai-service relay per ADR-0024, step 2 of the chantier (skeleton + tests against an in-process fake gRPC server; no live HTTP endpoint yet — that ships in the SSE-bridge PR). What ships: - contract/proto vendoring at apps/portal-bff/src/grpc/proto/apf-ai/ (common, chat, rag, ingestion, models). pnpm run grpc:sync refreshes from the sibling apf-ai-service tree; both .proto and the ts-proto TypeScript stubs under grpc/gen/ are committed for hermetic builds and reviewable diffs. - Codegen via pnpm run grpc:codegen using grpc-tools' bundled protoc and ts-proto (outputServices=grpc-js, esModuleInterop, forceLong, useOptionals=messages). Generated tree is excluded from Prettier and ESLint so hand-rules do not chase codegen output. - assertAiServiceConfig() pre-flight validator alongside the other config validators (AI_SERVICE_GRPC_ENDPOINT, AI_SERVICE_CLIENT_ID, AI_SERVICE_GRPC_TLS). - AiClientModule provides ChatClient / RagClient / IngestionClient / ModelsClient wrappers, a GrpcMetadataBuilder that stamps every call with x-client-id + x-correlation-id (W3C trace-id from OTel when a span is active, explicit override or UUID fallback otherwise), and a PrincipalMapper that hashes the Entra oid via HashUserIdService so the proto Principal.subject matches audit.events.actor_id_hash exactly (ADR-0013 cross-reference contract). - Specs cover: env validator, principal mapper, metadata builder (OTel span / override / fallback paths), chat client streaming + cancellation against an in-process fake gRPC ChatService, rag client unary happy path + error propagation, and module bootstrap. AiClientModule is NOT imported in AppModule yet — the SSE-bridge controller and its live route ship in the next PR.
79 lines
2.2 KiB
JavaScript
79 lines
2.2 KiB
JavaScript
import nx from '@nx/eslint-plugin';
|
|
|
|
export default [
|
|
...nx.configs['flat/base'],
|
|
...nx.configs['flat/typescript'],
|
|
...nx.configs['flat/javascript'],
|
|
{
|
|
"ignores": [
|
|
"**/dist",
|
|
"**/out-tsc",
|
|
"**/vitest.config.*.timestamp*",
|
|
// Generated gRPC TypeScript stubs — hand-rules do not apply to
|
|
// codegen output. Lint the wrapper modules in `ai-client/`, not
|
|
// the ts-proto-emitted files under `gen/`.
|
|
"apps/portal-bff/src/grpc/gen/**"
|
|
]
|
|
},
|
|
{
|
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
rules: {
|
|
'@nx/enforce-module-boundaries': [
|
|
'error',
|
|
{
|
|
enforceBuildableLibDependency: true,
|
|
allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'],
|
|
// Module boundaries per ADR-0003.
|
|
//
|
|
// Scope tags partition the workspace into front (portal-shell) /
|
|
// back (portal-bff) / shared (consumable by both). Type tags
|
|
// partition into apps / feature libs / shared libs.
|
|
//
|
|
// Both axes apply: a lib must satisfy both scope and type rules
|
|
// to be importable.
|
|
depConstraints: [
|
|
{
|
|
sourceTag: 'scope:portal-shell',
|
|
onlyDependOnLibsWithTags: ['scope:portal-shell', 'scope:shared'],
|
|
},
|
|
{
|
|
sourceTag: 'scope:portal-bff',
|
|
onlyDependOnLibsWithTags: ['scope:portal-bff', 'scope:shared'],
|
|
},
|
|
{
|
|
sourceTag: 'scope:shared',
|
|
onlyDependOnLibsWithTags: ['scope:shared'],
|
|
},
|
|
{
|
|
sourceTag: 'type:app',
|
|
onlyDependOnLibsWithTags: ['type:feature', 'type:shared'],
|
|
},
|
|
{
|
|
sourceTag: 'type:feature',
|
|
onlyDependOnLibsWithTags: ['type:feature', 'type:shared'],
|
|
},
|
|
{
|
|
sourceTag: 'type:shared',
|
|
onlyDependOnLibsWithTags: ['type:shared'],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'**/*.ts',
|
|
'**/*.tsx',
|
|
'**/*.cts',
|
|
'**/*.mts',
|
|
'**/*.js',
|
|
'**/*.jsx',
|
|
'**/*.cjs',
|
|
'**/*.mjs',
|
|
],
|
|
// Override or add rules here
|
|
rules: {},
|
|
},
|
|
];
|