Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# `error-codes` — stable error-code registry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the namespaced `TENANT_SUBDOMAIN.*` error-code registry plus the
concrete `SubdomainTenantResolverError` class for the
`@suaveplan/api-tenant-resolver-subdomain` package. Every public failure
path in the resolver constructs an error with one of these codes so
consumers can branch on a stable string value (e.g. `error.code ===
TENANT_SUBDOMAIN_CODES.BASE_DOMAIN_MISMATCH`) and `instanceof
SubdomainTenantResolverError` narrows `code` to the union type.
These codes back The host-side mapping (4xx vs 5xx response
shape) and the OTel `outcome` label so an operator dashboarding tenant
resolution sees the exact failure mode without parsing free-text
messages.
## Features
- Five stable codes: `INVALID`, `NOT_FOUND_IN_REGISTRY`,
`BASE_DOMAIN_MISMATCH`, `FORWARDED_HOST_UNTRUSTED`, `INVALID_OPTIONS`.
- `defineCodes` from `@suaveplan/error` — values are frozen and the
namespace prefix (`TENANT_SUBDOMAIN.`) is applied once.
- Subclass with public-constructor lint compliance and a redundant
`subdomainTenantResolverCode` field for downstream type narrowing.
## Usage: Basic
```ts
import {
SubdomainTenantResolverError,
TENANT_SUBDOMAIN_CODES,
} from "@suaveplan/api-tenant-resolver-subdomain/error-codes";
throw new SubdomainTenantResolverError(
TENANT_SUBDOMAIN_CODES.INVALID,
"Label 'ACME!' contains forbidden characters"
);
```
## Usage: Advanced
```ts
import {
SubdomainTenantResolverError,
TENANT_SUBDOMAIN_CODES,
} from "@suaveplan/api-tenant-resolver-subdomain/error-codes";
try {
await resolver.resolve(request);
} catch (e) {
if (e instanceof SubdomainTenantResolverError) {
switch (e.code) {
case TENANT_SUBDOMAIN_CODES.BASE_DOMAIN_MISMATCH:
return new Response("Wrong domain", { status: 400 });
case TENANT_SUBDOMAIN_CODES.FORWARDED_HOST_UNTRUSTED:
return new Response("Forbidden", { status: 403 });
default:
throw e;
}
}
throw e;
}
```
## API Reference
- `TENANT_SUBDOMAIN_CODES` — frozen record of code constants.
- `SubdomainTenantResolverErrorCode` — union of the registered codes.
- `SubdomainTenantResolverError` — concrete subclass of
`SuaveplanError<SubdomainTenantResolverErrorCode>`.
## Implementation Notes
The registry is built with `defineCodes("TENANT_SUBDOMAIN", [...] as const)`
so the namespace prefix is enforced once and every value is statically
typed as `TENANT_SUBDOMAIN.<NAME>`. The error subclass exposes a public
constructor (per the api-stack public-constructor lint rule) and carries
a `subdomainTenantResolverCode` field so callers that down-cast on the
narrow class don't have to re-narrow the inherited `code` field.

View File

@@ -0,0 +1,52 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
SubdomainTenantResolverError,
TENANT_SUBDOMAIN_CODES,
} from "./error-codes.js";
describe("error-codes", () => {
it("registers all five codes with the namespace prefix", {
timeout: 5000,
}, () => {
expect(TENANT_SUBDOMAIN_CODES.INVALID).toBe("TENANT_SUBDOMAIN.INVALID");
expect(TENANT_SUBDOMAIN_CODES.NOT_FOUND_IN_REGISTRY).toBe(
"TENANT_SUBDOMAIN.NOT_FOUND_IN_REGISTRY"
);
expect(TENANT_SUBDOMAIN_CODES.BASE_DOMAIN_MISMATCH).toBe(
"TENANT_SUBDOMAIN.BASE_DOMAIN_MISMATCH"
);
expect(TENANT_SUBDOMAIN_CODES.FORWARDED_HOST_UNTRUSTED).toBe(
"TENANT_SUBDOMAIN.FORWARDED_HOST_UNTRUSTED"
);
expect(TENANT_SUBDOMAIN_CODES.INVALID_OPTIONS).toBe(
"TENANT_SUBDOMAIN.INVALID_OPTIONS"
);
});
it("constructs an error carrying the discriminator code", {
timeout: 5000,
}, () => {
const e = new SubdomainTenantResolverError(
TENANT_SUBDOMAIN_CODES.INVALID,
"label '!' rejected"
);
expect(e).toBeInstanceOf(SubdomainTenantResolverError);
expect(e.code).toBe(TENANT_SUBDOMAIN_CODES.INVALID);
expect(e.subdomainTenantResolverCode).toBe(
TENANT_SUBDOMAIN_CODES.INVALID
);
expect(e.message).toContain("label");
});
it("propagates options.cause through SuaveplanError", {
timeout: 5000,
}, () => {
const cause = new Error("inner");
const e = new SubdomainTenantResolverError(
TENANT_SUBDOMAIN_CODES.INVALID_OPTIONS,
"outer",
{ cause }
);
expect(e.cause).toBe(cause);
});
});

View File

@@ -0,0 +1,52 @@
import type { SuaveplanErrorOptions } from "@suaveplan/error";
import { defineCodes, SuaveplanError } from "@suaveplan/error";
/**
* Stable error-code registry for `@suaveplan/api-tenant-resolver-subdomain`.
*
* Every public failure path raises a {@link SubdomainTenantResolverError}
* carrying one of these namespaced `TENANT_SUBDOMAIN.*` codes. Consumers
* branch on the string value
* (e.g. `error.code === TENANT_SUBDOMAIN_CODES.INVALID`).
*
* Codes:
* - `INVALID` — the extracted label fails the configured `validate`
* predicate (e.g. empty, contains a `/`, exceeds max length).
* - `NOT_FOUND_IN_REGISTRY` — strict mode + a `registry` callback was
* configured AND it returned `null` for the extracted slug.
* - `BASE_DOMAIN_MISMATCH` — strict mode + the inbound host does not
* end with the configured `baseDomain`.
* - `FORWARDED_HOST_UNTRUSTED` — strict mode + `X-Forwarded-Host`
* arrived from an upstream IP outside `trustedProxyCidrs`.
* - `INVALID_OPTIONS` — `createSubdomainTenantResolver` called with
* options that fail Zod validation at construction time.
*/
export const TENANT_SUBDOMAIN_CODES = defineCodes("TENANT_SUBDOMAIN", [
"INVALID",
"NOT_FOUND_IN_REGISTRY",
"BASE_DOMAIN_MISMATCH",
"FORWARDED_HOST_UNTRUSTED",
"INVALID_OPTIONS",
] as const);
export type SubdomainTenantResolverErrorCode =
(typeof TENANT_SUBDOMAIN_CODES)[keyof typeof TENANT_SUBDOMAIN_CODES];
/**
* Concrete error class for the api-tenant-resolver-subdomain package.
* Extends {@link SuaveplanError} so
* `instanceof SubdomainTenantResolverError` narrows `code` to
* {@link SubdomainTenantResolverErrorCode}.
*/
export class SubdomainTenantResolverError extends SuaveplanError<SubdomainTenantResolverErrorCode> {
public readonly subdomainTenantResolverCode: SubdomainTenantResolverErrorCode;
public constructor(
code: SubdomainTenantResolverErrorCode,
message: string,
options?: SuaveplanErrorOptions
) {
super(code, message, options);
this.subdomainTenantResolverCode = code;
}
}

View File

@@ -0,0 +1,5 @@
export {
SubdomainTenantResolverError,
type SubdomainTenantResolverErrorCode,
TENANT_SUBDOMAIN_CODES,
} from "./error-codes.js";