@arkyn/server 3.0.7 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +200 -0
- package/dist/http/api/_logRequest.d.ts.map +1 -1
- package/dist/index.js +200 -146
- package/dist/index.js.map +1 -1
- package/dist/modules/http/api/_logRequest.js +31 -28
- package/dist/modules/http/api/_logRequest.js.map +1 -1
- package/dist/modules/services/apiService.js +35 -23
- package/dist/modules/services/apiService.js.map +1 -1
- package/dist/modules/services/logService.js +29 -11
- package/dist/modules/services/logService.js.map +1 -1
- package/dist/modules/utilities/sensitiveDataKeys.js +31 -0
- package/dist/modules/utilities/sensitiveDataKeys.js.map +1 -0
- package/dist/services/apiService.d.ts +12 -0
- package/dist/services/apiService.d.ts.map +1 -1
- package/dist/services/logService.d.ts +15 -2
- package/dist/services/logService.d.ts.map +1 -1
- package/dist/utilities/sensitiveDataKeys.d.ts +22 -0
- package/dist/utilities/sensitiveDataKeys.d.ts.map +1 -0
- package/package.json +7 -6
package/AGENTS.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @arkyn/server — agent guide
|
|
2
|
+
|
|
3
|
+
Server-side building blocks for Remix/React Router loaders and actions, or any fetch-based backend: typed HTTP responses, request parsing, Zod-based schema validation, and validators for Brazilian documents and common fields. Use it instead of hand-rolling `Response.json(...)` calls, ad-hoc try/catch shapes, or one-off regex validators. Everything below is exact — signatures, throw behavior, and defaults — read directly from source, no need to open README.md or any other file for correct usage.
|
|
4
|
+
|
|
5
|
+
## Required setup
|
|
6
|
+
|
|
7
|
+
- ESM only: `import`, never `require()`.
|
|
8
|
+
- Peer deps: `zod` (required by `SchemaValidator`, `formParse`, `formAsyncParse`), `libphonenumber-js` (required only by `validatePhone`).
|
|
9
|
+
|
|
10
|
+
## Import convention — always prefer subpath imports
|
|
11
|
+
|
|
12
|
+
Prefer importing each class/function from its own subpath instead of the root barrel (`@arkyn/server`):
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { decodeRequestBody } from "@arkyn/server/decodeRequestBody";
|
|
16
|
+
import { SchemaValidator } from "@arkyn/server/schemaValidator";
|
|
17
|
+
import { Created } from "@arkyn/server/created";
|
|
18
|
+
import { errorHandler } from "@arkyn/server/errorHandler";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Naming rule**: the subpath is the export name with only its first letter lowercased — nothing else changes. `SchemaValidator` → `schemaValidator`, `ApiService` → `apiService`, `BadGateway` → `badGateway`; function exports that already start lowercase (`decodeRequestBody`, `validateCpf`, etc.) are unchanged. This is exact for every export in this package; each entry below states its own import line. There are no per-export CSS files in this package (no UI).
|
|
22
|
+
|
|
23
|
+
## Core pattern
|
|
24
|
+
|
|
25
|
+
Every route action/loader should follow this shape: decode the body, validate it, do the work, return a typed response, and let `errorHandler` translate anything thrown (including validation and business errors) into the right HTTP response.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { decodeRequestBody } from "@arkyn/server/decodeRequestBody";
|
|
29
|
+
import { SchemaValidator } from "@arkyn/server/schemaValidator";
|
|
30
|
+
import { Created } from "@arkyn/server/created";
|
|
31
|
+
import { errorHandler } from "@arkyn/server/errorHandler";
|
|
32
|
+
import { z } from "zod";
|
|
33
|
+
|
|
34
|
+
const createUserSchema = z.object({
|
|
35
|
+
email: z.string().email(),
|
|
36
|
+
name: z.string().min(2),
|
|
37
|
+
});
|
|
38
|
+
const userValidator = new SchemaValidator(createUserSchema);
|
|
39
|
+
|
|
40
|
+
export async function action({ request }: ActionFunctionArgs) {
|
|
41
|
+
try {
|
|
42
|
+
const body = await decodeRequestBody(request);
|
|
43
|
+
const data = userValidator.formValidate(body); // throws UnprocessableEntity with fieldErrors on failure
|
|
44
|
+
|
|
45
|
+
const user = await createUser(data);
|
|
46
|
+
return new Created("User created", { user }).toJson();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return errorHandler(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Throw the matching error class instead of returning ad-hoc error objects — `errorHandler` only recognizes `@arkyn/server`'s own classes (plus native `Response`), everything else falls back to a generic 500 `ServerError`.
|
|
54
|
+
|
|
55
|
+
## Success responses
|
|
56
|
+
|
|
57
|
+
All extend a common base exposing `.toResponse()` (returns a `Response` with `Content-Type: application/json`) and `.toJson()` (built on `Response.json()`, equivalent body). Constructor shape is the same for all except `NoContent`: `new X(message: string, body?: any)`. Response body is always `{ name, message, body }`.
|
|
58
|
+
|
|
59
|
+
| Class | Import | Status | Constructor | Notes |
|
|
60
|
+
|---|---|---|---|---|
|
|
61
|
+
| `Success` | `@arkyn/server/success` | 200 | `(message: string, body?: any)` | General success. |
|
|
62
|
+
| `Created` | `@arkyn/server/created` | 201 | `(message: string, body?: any)` | New resource created. |
|
|
63
|
+
| `Updated` | `@arkyn/server/updated` | 200 | `(message: string, body?: any)` | Semantically "updated", same shape as `Success`. |
|
|
64
|
+
| `Found` | `@arkyn/server/found` | 302 | `(message: string, body?: any)` | Resource located, included in body. |
|
|
65
|
+
| `NoContent` | `@arkyn/server/noContent` | 204 | `(message: string)` | Only `.toResponse()` exists (no `.toJson()`); body is always `null`. |
|
|
66
|
+
|
|
67
|
+
## Error responses
|
|
68
|
+
|
|
69
|
+
All extend a common base exposing the same `.toResponse()`/`.toJson()` pair. Constructor shape for all of these: `new X(message: string, cause?: any)` — `cause` is serialized to JSON and included in the response body as `{ name, message, cause }`. Throw them (don't return them directly) and let `errorHandler` convert to a `Response`.
|
|
70
|
+
|
|
71
|
+
| Class | Import | Status | Meaning |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `BadRequest` | `@arkyn/server/badRequest` | 400 | Malformed or invalid request. |
|
|
74
|
+
| `Unauthorized` | `@arkyn/server/unauthorized` | 401 | Missing/invalid credentials. |
|
|
75
|
+
| `Forbidden` | `@arkyn/server/forbidden` | 403 | Authenticated but not authorized. |
|
|
76
|
+
| `NotFound` | `@arkyn/server/notFound` | 404 | Resource doesn't exist. |
|
|
77
|
+
| `Conflict` | `@arkyn/server/conflict` | 409 | Conflicts with current server state (e.g. duplicate). |
|
|
78
|
+
| `ServerError` | `@arkyn/server/serverError` | 500 | Unexpected server-side failure. |
|
|
79
|
+
| `BadGateway` | `@arkyn/server/badGateway` | 502 | Upstream server returned an invalid response. |
|
|
80
|
+
| `NotImplemented` | `@arkyn/server/notImplemented` | 501 | Functionality not yet supported. |
|
|
81
|
+
|
|
82
|
+
### `UnprocessableEntity` (422) — special shape
|
|
83
|
+
|
|
84
|
+
- Import: `import { UnprocessableEntity } from "@arkyn/server/unprocessableEntity";`
|
|
85
|
+
|
|
86
|
+
Constructor takes a single object, not `(message, cause)`:
|
|
87
|
+
```typescript
|
|
88
|
+
new UnprocessableEntity({
|
|
89
|
+
message?: string; // default: "Unprocessable entity"
|
|
90
|
+
fieldErrors?: Record<string, string>; // per-field validation messages
|
|
91
|
+
fields?: Record<string, string>; // original submitted values, for repopulating forms
|
|
92
|
+
data?: any; // extra data merged into the response cause
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
Response body: `{ name: "UnprocessableEntity", message, cause: { data, fieldErrors, fields } }`. This is what `SchemaValidator.formValidate`/`formAsyncValidate` throw automatically — you rarely construct it by hand.
|
|
96
|
+
|
|
97
|
+
## Validation
|
|
98
|
+
|
|
99
|
+
### SchemaValidator
|
|
100
|
+
- Import: `import { SchemaValidator } from "@arkyn/server/schemaValidator";`
|
|
101
|
+
```typescript
|
|
102
|
+
const validator = new SchemaValidator(schema); // constructor(schema: T extends ZodType)
|
|
103
|
+
|
|
104
|
+
validator.isValid(data: any): boolean // never throws
|
|
105
|
+
validator.safeValidate(data: any): z.ZodSafeParseResult<z.infer<T>> // never throws, full Zod result
|
|
106
|
+
validator.validate(data: any): z.infer<T> // throws ServerError (trusted/internal data)
|
|
107
|
+
validator.formValidate(data: any, message?: string): z.infer<T> // throws UnprocessableEntity (user input)
|
|
108
|
+
validator.formAsyncValidate(data: any, message?: string): Promise<z.infer<T>> // async refinements
|
|
109
|
+
```
|
|
110
|
+
`formValidate`/`formAsyncValidate` throw `UnprocessableEntity` with `fields`/`fieldErrors` populated from the Zod result, plus `data: { scrollTo: firstErrorFieldName }`.
|
|
111
|
+
|
|
112
|
+
### formParse
|
|
113
|
+
- Import: `import { formParse } from "@arkyn/server/formParse";`
|
|
114
|
+
- Signature: `formParse<T>([formData, schema]: [Record<string, any>, ZodType]): { success: true; data } | { success: false; fieldErrors; fields }`
|
|
115
|
+
- Functional, synchronous, never throws — the return-shape equivalent of `formValidate` without the class wrapper.
|
|
116
|
+
|
|
117
|
+
### formAsyncParse
|
|
118
|
+
- Import: `import { formAsyncParse } from "@arkyn/server/formAsyncParse";`
|
|
119
|
+
- Signature: `formAsyncParse<T>([formData, schema]: [Record<string, any>, ZodType]): Promise<{ success: true; data } | { success: false; fieldErrors; fields }>`
|
|
120
|
+
- Async variant using `schema.safeParseAsync`, for schemas with async refinements (e.g. uniqueness checks against a database).
|
|
121
|
+
|
|
122
|
+
## Brazilian document validators
|
|
123
|
+
|
|
124
|
+
All `(value: string) => boolean`, never throw:
|
|
125
|
+
|
|
126
|
+
- `validateCpf` — `import { validateCpf } from "@arkyn/server/validateCpf";` — strips formatting, checks 11 digits, rejects repeated-digit sequences, verifies both check digits.
|
|
127
|
+
- `validateCnpj` — `import { validateCnpj } from "@arkyn/server/validateCnpj";` — strips formatting, checks 14 digits, rejects repeated-digit sequences, verifies both check digits.
|
|
128
|
+
- `validateCep` — `import { validateCep } from "@arkyn/server/validateCep";` — exactly 8 numeric digits, formatted or not.
|
|
129
|
+
- `validateRg` — `import { validateRg } from "@arkyn/server/validateRg";` — `(rawRg: string): boolean`, generic structural check: removes non-alphanumeric characters, requires length 7–9, optionally allows a trailing letter verifier (no state-specific format support).
|
|
130
|
+
|
|
131
|
+
## Generic validators
|
|
132
|
+
|
|
133
|
+
- `validateEmail` — `import { validateEmail } from "@arkyn/server/validateEmail";` — `(email: string): Promise<boolean>`, async. Checks basic format, RFC 5322 syntax, **and** DNS MX/A/AAAA resolution of the domain — a syntactically valid but non-existent domain returns `false`.
|
|
134
|
+
- `validatePassword` — `import { validatePassword } from "@arkyn/server/validatePassword";` — `(rawPassword: string): boolean`, at least 8 chars, 1 uppercase, 1 letter, 1 number, 1 special character.
|
|
135
|
+
- `validatePhone` — `import { validatePhone } from "@arkyn/server/validatePhone";` — `(rawPhone: string): boolean`, parses with `libphonenumber-js`, then confirms the resolved country is in `@arkyn/templates`'s country list.
|
|
136
|
+
- `validateDate` — `import { validateDate } from "@arkyn/server/validateDate";` — `(date: string, config?: { inputFormat?: "brazilianDate" | "isoDate" | "timestamp"; minYear?: number; maxYear?: number }): boolean`. `inputFormat` default `"brazilianDate"` (`DD/MM/YYYY`; `"isoDate"` is `MM-DD-YYYY`, `"timestamp"` is `YYYY-MM-DD`), `minYear` default `1900`, `maxYear` default `3000`. Rejects invalid calendar dates (e.g. Feb 29 on a non-leap year).
|
|
137
|
+
|
|
138
|
+
## Request utilities
|
|
139
|
+
|
|
140
|
+
#### decodeRequestBody
|
|
141
|
+
- Import: `import { decodeRequestBody } from "@arkyn/server/decodeRequestBody";`
|
|
142
|
+
- Signature: `decodeRequestBody(request: Request): Promise<any>`
|
|
143
|
+
- Reads the raw body, tries `JSON.parse` first, then falls back to `URLSearchParams` (only if the text contains `=`). Throws `BadRequest` if neither parse succeeds.
|
|
144
|
+
|
|
145
|
+
#### getScopedParams
|
|
146
|
+
- Import: `import { getScopedParams } from "@arkyn/server/getScopedParams";`
|
|
147
|
+
- Signature: `getScopedParams(request: Request, scope: string = ""): URLSearchParams`
|
|
148
|
+
- Without `scope`, returns `request`'s search params unmodified. With `scope`, returns only params prefixed `"${scope}:"`, with that prefix stripped from each key — e.g. scope `"table"` turns `?table:page=2` into a params object where `.get("page")` is `"2"`.
|
|
149
|
+
|
|
150
|
+
#### decodeRequestErrorMessage
|
|
151
|
+
- Import: `import { decodeRequestErrorMessage } from "@arkyn/server/decodeRequestErrorMessage";`
|
|
152
|
+
- Signature: `decodeRequestErrorMessage(data: any, response: Response): string`
|
|
153
|
+
- Extracts a human message from an API error payload, checking in order: `data.message`, `data.operator_erro_message`, `data.error`, `data.error.message`, `response.statusText`, falling back to `"Missing error message"`.
|
|
154
|
+
|
|
155
|
+
#### errorHandler
|
|
156
|
+
- Import: `import { errorHandler } from "@arkyn/server/errorHandler";`
|
|
157
|
+
- Signature: `errorHandler(error: any): Response`
|
|
158
|
+
- Catch-all for route actions/loaders. Recognizes all `@arkyn/server` success/error classes (calls `.toJson()`/`.toResponse()` on them) and native `Response` objects (returned as-is); anything else is wrapped in a `ServerError` 500.
|
|
159
|
+
|
|
160
|
+
#### flushDebugLogs
|
|
161
|
+
- Import: `import { flushDebugLogs } from "@arkyn/server/flushDebugLogs";`
|
|
162
|
+
- Signature: `flushDebugLogs(props: { name: string; scheme: "yellow" | "cyan" | "red" | "green"; debugs: string[] }): void`
|
|
163
|
+
- Prints colored `[name] line` output to `console.log`, one line per `debugs` entry. No-op unless `process.env.NODE_ENV === "development"` or `process.env.DEBUG_MODE === "true"`.
|
|
164
|
+
|
|
165
|
+
## Services
|
|
166
|
+
|
|
167
|
+
#### ApiService
|
|
168
|
+
- Import: `import { ApiService } from "@arkyn/server/apiService";`
|
|
169
|
+
```typescript
|
|
170
|
+
new ApiService({
|
|
171
|
+
baseUrl: string;
|
|
172
|
+
baseHeaders?: HeadersInit; // merged into every request
|
|
173
|
+
baseToken?: string | null; // default Bearer token; overridable per-call via data.token
|
|
174
|
+
enableDebug?: boolean; // logs request/response via flushDebugLogs when true
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
api.get(endpoint: string, data?: { headers?; token?; urlParams?: Record<string,string> })
|
|
178
|
+
api.post(endpoint: string, data?: { body?; headers?; token?; urlParams? })
|
|
179
|
+
api.put(endpoint: string, data?: { body?; headers?; token?; urlParams? })
|
|
180
|
+
api.patch(endpoint: string, data?: { body?; headers?; token?; urlParams? })
|
|
181
|
+
api.delete(endpoint: string, data?: { body?; headers?; token?; urlParams? })
|
|
182
|
+
```
|
|
183
|
+
Every method returns whatever the underlying request helper resolves (an object including at least `status` and `message`). `token`/`urlParams` per-call override/extend the instance defaults; `Authorization: Bearer <token>` header is set automatically when a token is available.
|
|
184
|
+
|
|
185
|
+
#### DebugService (static)
|
|
186
|
+
- Import: `import { DebugService } from "@arkyn/server/debugService";`
|
|
187
|
+
- `DebugService.setIgnoreFile(file: string): void` — skip stack frames from files matching `file` (e.g. an internal adapter) so debug logs show the real caller.
|
|
188
|
+
- `DebugService.clearIgnoreFiles(): void`
|
|
189
|
+
- `DebugService.getCaller(): { functionName: string; callerInfo: string }` — used internally by the response classes' debug output; rarely called directly.
|
|
190
|
+
|
|
191
|
+
#### LogService (static)
|
|
192
|
+
- Import: `import { LogService } from "@arkyn/server/logService";`
|
|
193
|
+
- `LogService.setConfig(config: { trafficSourceId: string; userToken: string; logBaseApiUrl?: string }): void` — only applies on the **first** call; later calls are silently ignored until `resetConfig()`.
|
|
194
|
+
- `LogService.getConfig(): { trafficSourceId; userToken; apiUrl } | undefined`
|
|
195
|
+
- `LogService.resetConfig(): void`
|
|
196
|
+
|
|
197
|
+
## Related packages
|
|
198
|
+
|
|
199
|
+
- `@arkyn/shared` — reused internally for formatting/validation primitives (e.g. `ValidateDateService` backs `validateDate`); safe to import directly for the same formatting on the client.
|
|
200
|
+
- `@arkyn/templates` — country/locale data backing `validatePhone`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_logRequest.d.ts","sourceRoot":"","sources":["../../../src/http/api/_logRequest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_logRequest.d.ts","sourceRoot":"","sources":["../../../src/http/api/_logRequest.ts"],"names":[],"mappings":"AAKA,KAAK,WAAW,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,iBAAe,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA4F5D;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|