first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# error-codes
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the stable error-code registry and typed `MacOSContactsError` class for `@suaveplan/macos-contacts`. Every failure path the package exposes raises a `MacOSContactsError` carrying one of six registered codes (`INVALID_INPUT`, `CONTACTS_NOT_FOUND`, `CONTACTS_PERMISSION_DENIED`, `CONTACTS_ACCESS_FAILED`, `CONTACTS_PARSE_FAILED`, `CONTACTS_UNAVAILABLE`). Codes are namespaced strings (`"MACOS_CONTACTS.CONTACTS_PERMISSION_DENIED"`) so consumers can dispatch programmatically without parsing messages, and the typed error narrows the `query` / `groupName` / `contactId` discriminants for diagnostics.
## Features
- `MACOS_CONTACTS_CODES` — `defineCodes("MACOS_CONTACTS", [...])` registry; the canonical source of truth for every code emitted by the package.
- `MacOSContactsError extends SuaveplanError<MacOSContactsErrorCode>` — typed error class. `instanceof` narrows `code` to a literal-string union.
- `MacOSContactsErrorCode` — union of every registered code.
- `MacOSContactsErrorOptions` — options bag extending `SuaveplanErrorOptions` with `query`, `groupName`, `contactId` discriminants and standard `cause` chaining.
- Replaces upstream's `HttpError(... code as unknown as HttpErrorCode)` cast: codes flow through the type system end-to-end, with no `unknown` escape hatches.
## Usage: Basic
```ts
import {
MACOS_CONTACTS_CODES,
MacOSContactsError,
} from "@suaveplan/macos-contacts";
function ensurePermission(stderr: string) {
if (/not authorized/i.test(stderr)) {
throw new MacOSContactsError(
MACOS_CONTACTS_CODES.CONTACTS_PERMISSION_DENIED,
"macOS denied access to Contacts.app",
);
}
}
```
## Usage: Advanced
```ts
import {
MACOS_CONTACTS_CODES,
MacOSContactsError,
} from "@suaveplan/macos-contacts";
try {
await bridge.searchContacts(query);
} catch (err) {
if (err instanceof MacOSContactsError) {
switch (err.code) {
case MACOS_CONTACTS_CODES.CONTACTS_PERMISSION_DENIED:
return openSystemSettingsDialog();
case MACOS_CONTACTS_CODES.INVALID_INPUT:
return showValidationToast(err.message);
default:
throw err;
}
}
throw err;
}
```
## API Reference
### `MACOS_CONTACTS_CODES`
A frozen registry produced by `defineCodes("MACOS_CONTACTS", [...] as const)`. Each entry maps a bare key (e.g. `CONTACTS_PERMISSION_DENIED`) to its namespaced string (`"MACOS_CONTACTS.CONTACTS_PERMISSION_DENIED"`).
### `MacOSContactsErrorCode`
Union type `(typeof MACOS_CONTACTS_CODES)[keyof typeof MACOS_CONTACTS_CODES]`. Use to type-check `error.code` in catch blocks.
### `MacOSContactsError(code, message, options?)`
Constructor parameters:
- `code: MacOSContactsErrorCode` — required, the discriminant.
- `message: string` — required, English diagnostic.
- `options.query?: string` — attaches `error.query`.
- `options.groupName?: string` — attaches `error.groupName`.
- `options.contactId?: string` — attaches `error.contactId`.
- `options.cause?: unknown` — standard ES `Error.cause`; preserved through serialization and span events.
## Implementation Notes
The discriminants live as direct instance properties so consumers can do `if (err.query)` without an extra unwrap. Codes are validated at registry time by `defineCodes`, which freezes the result and enforces unique namespace tokens.
Adding a new code requires three concurrent edits: append the literal to the `MACOS_CONTACTS_CODES` array, raise it from at least one production call site, and add a test that round-trips an instance with the new code. The package's own `macos-contacts.spec-conformance.test.ts` enforces the "every code referenced" rule — removing a code to placate a gate is banned per `AGENTS.md` §2c.
Error-code strings are part of the public API. Renaming one is a breaking change. Add a new code instead and let consumers migrate explicitly.