@kalutskii/foundation 0.7.1 → 0.7.3

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.
Files changed (3) hide show
  1. package/README.md +274 -208
  2. package/dist/index.d.ts +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,18 +1,10 @@
1
1
  # @kalutskii/foundation
2
2
 
3
- Collection of utilities, types, and helpers for building typed API contracts and responses in TypeScript projects.
4
- Designed for use in Bun/Node.js and Cloudflare Workers environments.
3
+ Shared TypeScript foundation for contracts, schemas, framework adapters, and reusable utilities.
4
+ The package is designed for Bun, Node.js, Hono applications, and Cloudflare Workers environments.
5
5
 
6
- ## What this package provides
7
-
8
- - Typed API response contracts with success/failure factories and safe resolvers.
9
- - Hono helpers: typed JSON responses, global error handler, request logging middleware.
10
- - Execution utilities: safe async execution and execution time measurement.
11
- - Datetime utilities: timezone-aware formatting helpers.
12
- - Logging utility: unified colored log interface.
13
- - Random string generation utility.
14
- - JWT service with optional Zod schema validation.
15
- - Zod helpers for query param coercion, pagination schemas, nested schema flattening, and type utilities.
6
+ This repository is not intended to document every exported function through standalone usage snippets.
7
+ Public JSDoc, generated declarations, colocated specifications, and editor inference are the API reference.
16
8
 
17
9
  ## Installation
18
10
 
@@ -20,272 +12,346 @@ Designed for use in Bun/Node.js and Cloudflare Workers environments.
20
12
  bun add @kalutskii/foundation
21
13
  ```
22
14
 
23
- or
24
-
25
15
  ```bash
26
- npm i @kalutskii/foundation
16
+ npm install @kalutskii/foundation
27
17
  ```
28
18
 
29
- ## Core concepts
19
+ ## Package scope
20
+
21
+ The package contains several deliberately isolated areas:
22
+
23
+ | Module | Responsibility |
24
+ | ---------------- | ----------------------------------------------------------------------------------------- |
25
+ | `utilities` | Framework-independent datetime, enum, execution, generation, logging, and type utilities. |
26
+ | `http` | Shared HTTP result contracts, factories, status constants, and result resolvers. |
27
+ | `zod-validation` | Generic Zod parsing, validation, refinement, and related type utilities. |
28
+ | `zod-search` | Reusable search and pagination contracts composed from lower-level Zod primitives. |
29
+ | `zod-bulk` | Include/exclude selection contracts shared by frontend and backend bulk operations. |
30
+ | `hono` | Hono-specific response, file response, error handling, and request logging adapters. |
31
+ | `drizzle` | Drizzle-specific SQL composition that must not leak into generic contract modules. |
32
+ | `zod-jwt` | JWT service integration with optional Zod validation of decoded payloads. |
33
+
34
+ The root `src/index.ts` is the only public package entrypoint. Internal file paths are implementation details
35
+ and should not be imported directly by consumers of `@kalutskii/foundation`.
36
+
37
+ ## Architecture
38
+
39
+ ### Dependency hierarchy
40
+
41
+ Dependencies must flow from specialized components toward smaller and more generic foundations:
42
+
43
+ ```text
44
+ Framework adapters
45
+ hono / drizzle / zod-jwt
46
+
47
+
48
+ Contract composition
49
+ zod-search / zod-bulk
50
+
51
+
52
+ Contract primitives
53
+ http / zod-validation
54
+
55
+
56
+ Generic utilities
57
+ utilities
58
+ ```
30
59
 
31
- Contract shape:
60
+ This diagram defines direction, not a requirement for every module to depend on the layer below it.
61
+ Independent modules should remain independent instead of introducing artificial shared abstractions.
32
62
 
33
- - Success: `{ kind: 'data', status, data }`
34
- - Error: `{ kind: 'error', status, error }`
63
+ ### Dependency rules
35
64
 
36
- Status code groups are exported as constants and used by types:
65
+ 1. Generic utilities must not depend on Hono, Drizzle, JWT services, or application contracts.
66
+ 2. Generic Zod modules must not acquire hidden framework dependencies through imported helper types.
67
+ 3. Framework adapters may consume contracts and utilities, but contracts must not import adapters back.
68
+ 4. Sibling modules should communicate through public concepts instead of reaching into private internals.
69
+ 5. External dependencies should stay inside the narrowest module that genuinely requires their behavior.
70
+ 6. Type-only dependencies follow the same architectural rules as runtime dependencies.
71
+ 7. A reusable abstraction belongs to the lowest layer that can own it without knowing its consumers.
37
72
 
38
- - `SUCCESS_STATUS_CODES` `[200, 201, 202, 307]`
39
- - `EXCEPTION_STATUS_CODES` `[400, 401, 403, 404, 405, 409, 500]`
73
+ A dependency that appears only in generated declarations is still a real dependency and must be reviewed.
74
+ For example, a generic Zod type importing `Simplify` from Drizzle would violate the hierarchy even at runtime zero.
40
75
 
41
- ## Usage
76
+ ### Responsibility boundaries
42
77
 
43
- ### 1. Build typed contracts
78
+ Each module owns one coherent domain. A module may contain several files, but every file must have a narrow role.
79
+ Avoid generic dumping grounds such as `common`, `helpers`, `shared`, `misc`, or `stuff` without a concrete domain.
44
80
 
45
- ```typescript
46
- import { failure, success } from '@kalutskii/foundation';
47
- import type { APIContractResult } from '@kalutskii/foundation';
81
+ A change belongs in an existing module only when that module can explain and enforce its complete semantics.
82
+ Otherwise, create a dedicated module instead of growing an unrelated file with another responsibility.
48
83
 
49
- type User = { id: string; name: string };
84
+ ## Project structure
50
85
 
51
- const ok = success<User>({ status: 200, data: { id: '1', name: 'Kate' } });
52
- const bad = failure({ status: 404, error: 'User not found' });
53
-
54
- const result: APIContractResult<User> = Math.random() > 0.5 ? ok : bad;
86
+ ```text
87
+ src/
88
+ ├── drizzle/
89
+ ├── hono/
90
+ ├── http/
91
+ ├── utilities/
92
+ ├── zod-bulk/
93
+ ├── zod-jwt/
94
+ ├── zod-search/
95
+ ├── zod-validation/
96
+ └── index.ts
55
97
  ```
56
98
 
57
- ### 2. Resolve fetchers safely
99
+ Modules are directories. Files inside them follow the `<module>.<responsibility>.ts` pattern:
58
100
 
59
- ```typescript
60
- import { fetchAndThrow, fetchSafely } from '@kalutskii/foundation';
61
- import type { APIContractResult } from '@kalutskii/foundation';
62
-
63
- type User = { id: string; name: string };
64
-
65
- declare function getUser(): Promise<APIContractResult<User>>;
66
-
67
- const safe = await fetchSafely(getUser);
68
- if (safe.error) {
69
- console.error(safe.error);
70
- } else {
71
- console.log(safe.data.name);
72
- }
73
-
74
- try {
75
- const user = await fetchAndThrow(getUser);
76
- console.log(user.name);
77
- } catch (error) {
78
- console.error(error);
79
- }
101
+ ```text
102
+ zod-search/
103
+ ├── zod-search.pagination.schemas.ts
104
+ ├── zod-search.schemas.ts
105
+ ├── zod-search.types.ts
106
+ └── zod-search.spec.ts
80
107
  ```
81
108
 
82
- ### 3. Use typed Hono JSON responses
109
+ Do not add folder barrels unless the package exposes a real subpath for that module. The root barrel remains the
110
+ single public API boundary and should export only symbols intentionally supported across package versions.
83
111
 
84
- ```typescript
85
- import { respond } from '@kalutskii/foundation';
86
- import { Hono } from 'hono';
112
+ ## File responsibilities
87
113
 
88
- const app = new Hono();
114
+ | Suffix | Expected content |
115
+ | ---------------- | ----------------------------------------------------------------------------- |
116
+ | `*.constants.ts` | Immutable values and literal collections without behavior. |
117
+ | `*.schemas.ts` | Runtime Zod schemas and factories whose result is a schema. |
118
+ | `*.types.ts` | Type aliases, interfaces, generic contracts, and schema-derived output types. |
119
+ | `*.factory.ts` | Functions whose primary responsibility is constructing non-schema values. |
120
+ | `*.resolvers.ts` | Functions that unwrap, normalize, or translate an existing result. |
121
+ | `*.utilities.ts` | Stateless reusable behavior that has no narrower architectural owner. |
122
+ | `*.parsing.ts` | Input parsing and preprocessing before domain validation. |
123
+ | `*.refiners.ts` | Refinement logic that narrows or safely composes an existing value. |
124
+ | `*.execution.ts` | Framework lifecycle execution and error-boundary behavior. |
125
+ | `*.logging.ts` | Logging formatters, sinks, or middleware behavior. |
126
+ | `*.respond.ts` | Framework response construction and response-specific contracts. |
127
+ | `*.spec.ts` | The single colocated runtime and compile-time specification for a module. |
89
128
 
90
- app.get('/health', (c) => {
91
- return respond(c, {
92
- status: 200,
93
- data: { ok: true },
94
- });
95
- });
96
- ```
129
+ Do not place TypeScript-only contracts in a schema file when they can be separated without creating a circular
130
+ responsibility. Do not split tiny files mechanically either: separation must communicate ownership, not line count.
97
131
 
98
- `respond` wraps `c.json` with a typed success payload and includes `APIError` in the route's output union.
132
+ ## Naming conventions
99
133
 
100
- ### 4. Wire up Hono error handler and logging middleware
134
+ ### Files and directories
101
135
 
102
- ```typescript
103
- import { honoLoggingHandler, onHandlerError } from '@kalutskii/foundation';
104
- import { Hono } from 'hono';
136
+ - Module directories use kebab case: `zod-search`, `zod-validation`.
137
+ - Source files repeat the module name and add a responsibility suffix: `http.resolvers.ts`.
138
+ - Specifications use the module name and `.spec.ts`: `hono.spec.ts`, `zod-bulk.spec.ts`.
139
+ - Avoid names that describe implementation history, temporary state, or vague grouping.
105
140
 
106
- const app = new Hono();
141
+ ### Runtime symbols
107
142
 
108
- app.use('*', honoLoggingHandler);
109
- app.onError(onHandlerError);
110
- ```
143
+ - Zod schema values use the `zod<Name>Schema` form: `zodPaginationSchema`.
144
+ - Schema factories keep the same form when the project already treats them as schema constructors.
145
+ - Functions use an explicit verb describing their effect: `parseQueryValue`, `generateRandomString`.
146
+ - Predicates begin with `is`, `has`, or `can` and must provide a meaningful type guard when possible.
147
+ - Constants use `UPPER_SNAKE_CASE` when they represent fixed configuration or enumerated values.
148
+ - Boolean options describe capability or state: `queryEnabled`, `paginationEnabled`, `withTime`.
111
149
 
112
- `onHandlerError` catches all thrown errors, maps `HTTPException` (< 500) to their status codes, and returns a generic 500 with a unique error ID for unexpected errors.
150
+ ### Type symbols
113
151
 
114
- `honoLoggingHandler` logs each request with method, status, duration, path, and query params.
115
- Example: `[12:12:12 (+4 UTC)] hono | POST 200 123ms /api/v1/users (search=term)`
152
+ - Exported types use PascalCase and describe the domain concept instead of its implementation.
153
+ - Zod-related public types use the established `Zod<Name>` prefix where it clarifies ownership.
154
+ - Option objects end with `Options`; result wrappers end with `Result` when that is their semantic role.
155
+ - Generic parameters use a `T` prefix and a meaningful noun: `TShape`, `TIdentifier`, `TQueryEnabled`.
156
+ - Literal generic flags should remain literal through `const` generics when they change the returned static shape.
116
157
 
117
- ### 5. Execute functions safely and measure performance
158
+ Names should answer what a symbol represents without requiring a reader to inspect its implementation.
159
+ Prefer a slightly longer precise name over a short generic name that loses domain meaning.
118
160
 
119
- ```typescript
120
- import { measureExecutionTime, safeExecute } from '@kalutskii/foundation';
161
+ ## TypeScript and code style
121
162
 
122
- const result = await safeExecute(
123
- () => fetchData(),
124
- (error) => console.error(error)
125
- );
163
+ The project uses strict TypeScript, ESM, Prettier, and type-aware ESLint. Generated declarations are part of the
164
+ public product, so a solution is incomplete when runtime behavior works but emitted types become broad or unstable.
126
165
 
127
- const { result: data, executionTime } = await measureExecutionTime(() => fetchData());
128
- console.log(`Done in ${executionTime}ms`);
129
- ```
166
+ ### Imports
130
167
 
131
- ### 6. Parse query params with `asQuery`
168
+ 1. External dependencies come first.
169
+ 2. Cross-module project imports use the `@/` alias.
170
+ 3. Same-module imports use relative paths.
171
+ 4. Type-only dependencies use `import type`.
172
+ 5. Import groups are separated by blank lines and left to the configured formatter for sorting.
132
173
 
133
- ```typescript
134
- import { asQuery } from '@kalutskii/foundation';
135
- import { z } from 'zod';
174
+ ### General code rules
136
175
 
137
- const schema = z.object({
138
- page: asQuery(z.number().int().positive()),
139
- isActive: asQuery(z.boolean()),
140
- });
176
+ - Prefer precise types over `any`, broad records, or type assertions that hide lost inference.
177
+ - Keep assertions close to the compiler limitation they solve and explain why they are safe.
178
+ - Use early returns when they reduce nesting and make exceptional paths visible.
179
+ - Separate logical phases with blank lines instead of compressing unrelated operations together.
180
+ - Reuse existing project dependencies and abstractions before introducing another package.
181
+ - Do not silently swallow unknown fields at public boundaries unless stripping is intentional and documented.
182
+ - Do not manually duplicate a runtime schema as an interface that can drift from validation behavior.
183
+ - Keep transforms, coercion, defaults, and input/output differences visible in both types and specifications.
141
184
 
142
- schema.parse({ page: '2', isActive: 'true' }); // { page: 2, isActive: true }
143
- ```
185
+ ### Zod rules
144
186
 
145
- `asQuery` preprocesses string values from query parameters coercing `'true'`/`'false'` to booleans and numeric strings to numbers — before passing them to the Zod schema for validation.
187
+ - Runtime schemas are the source of truth; public data types should normally use `z.infer` or `z.output`.
188
+ - Accept `z.ZodObject<TShape>` when object methods or exact keys are required by the implementation.
189
+ - Do not widen an object schema to `z.ZodType` merely to make a generic signature easier to write.
190
+ - Prepared schemas may use explicit escape hatches when transforms or refinements must remain untouched.
191
+ - Literal feature flags must alter both runtime shape and inferred output instead of producing vague optional fields.
192
+ - API boundary objects should usually be strict so stale or misspelled fields fail loudly.
146
193
 
147
- ### 7. Build flat query schemas from nested objects
194
+ ## Comments
148
195
 
149
- ```typescript
150
- import { asQuerySchema } from '@kalutskii/foundation';
151
- import { z } from 'zod';
152
-
153
- const schema = asQuerySchema(
154
- z.object({
155
- search: z.string().optional(),
156
- sort: z
157
- .object({
158
- field: z.string(),
159
- order: z.enum(['asc', 'desc']),
160
- })
161
- .optional(),
162
- })
163
- );
164
-
165
- schema.parse({ search: 'foo', field: 'name', order: 'asc' });
166
- // { search: 'foo', field: 'name', order: 'asc' }
167
-
168
- // Rejects the original nested structure
169
- schema.parse({ sort: { field: 'name', order: 'asc' } }); // throws
170
- ```
196
+ Comments are an intentional part of this codebase. They should make constraints and architectural reasoning easier
197
+ to recover later, especially around type-level behavior, validation order, side effects, and framework integration.
198
+
199
+ ### Inline comments
171
200
 
172
- `asQuerySchema` flattens nested `z.object(...)` fields to the top level, making it suitable for flat query parameter validation. Nested object fields are lifted, while optionality is preserved: required parents keep their fields required, optional parents make all promoted fields optional. The result is a strict schema (unknown keys are rejected).
201
+ Use inline comments to explain why code exists, what invariant it protects, or why a simpler-looking alternative is
202
+ incorrect. Avoid comments that merely translate the following statement into English.
173
203
 
174
- ### 8. Build reusable search schemas
204
+ Long files and specifications may use wide visual sections:
175
205
 
176
206
  ```typescript
177
- import { zodPaginationSchema, zodSearchSchema } from '@kalutskii/foundation';
178
- import { z } from 'zod';
207
+ // ==========================================================================================
208
+ // PREPARED SCHEMA COMPOSITION
209
+ // ==========================================================================================
210
+ ```
179
211
 
180
- const assetSchema = z.object({
181
- status: z.enum(['active', 'archived']),
182
- categoryId: z.number(),
183
- });
212
+ Keep separator width consistent within the same file. Section names should describe behavior or responsibility,
213
+ not generic chronology such as `STEP 1` or `OTHER`.
184
214
 
185
- const assetSearchSchema = zodSearchSchema({
186
- filters: assetSchema.pick({ status: true, categoryId: true }),
187
- });
215
+ For local reasoning, prefer two balanced lines that visually form a compact block:
188
216
 
189
- type AssetSearch = z.infer<typeof assetSearchSchema>;
217
+ ```typescript
218
+ // TypeScript cannot connect the generic conditional type with this runtime branch.
219
+ // The assertion preserves literal inference without replacing the parsed output type.
220
+ ```
190
221
 
191
- assetSearchSchema.parse({
192
- where: { status: 'active' },
193
- query: 'asset name',
194
- pagination: { offset: 0, limit: 20 },
195
- });
222
+ Leave a blank line after a reasoning block when it introduces the next logical phase. Dense comments should improve
223
+ scanning and grouping; they should not turn straightforward code into a narrated transcript.
196
224
 
197
- // Pagination remains available as a standalone reusable schema.
198
- zodPaginationSchema.parse({ offset: '0', limit: '20' });
199
- // { offset: 0, limit: 20 }
200
- ```
225
+ ## JSDoc
226
+
227
+ Every public symbol with non-obvious behavior should have JSDoc. Public schema factories, services, result types,
228
+ configuration types, and framework adapters require documentation before being exported from `src/index.ts`.
201
229
 
202
- ### 9. Share bulk selection between frontend and backend
230
+ The preferred visual style contains at least two meaningful lines of similar length:
203
231
 
204
232
  ```typescript
205
- import { zodBulkSelectionSchema, zodSearchSchema } from '@kalutskii/foundation';
206
- import { z } from 'zod';
207
-
208
- const assetBulkSelectionSchema = zodBulkSelectionSchema({
209
- identifierSchema: z.string().min(1),
210
- });
211
-
212
- const assetBulkFilterSchema = zodSearchSchema({
213
- filters: assetSchema.pick({ status: true, categoryId: true }),
214
- paginationEnabled: false,
215
- });
216
-
217
- const assetBulkRequestSchema = z
218
- .object({
219
- selection: assetBulkSelectionSchema,
220
- filter: assetBulkFilterSchema,
221
- })
222
- .strict();
223
-
224
- type AssetBulkRequest = z.infer<typeof assetBulkRequestSchema>;
233
+ /**
234
+ * Builds a strict selection contract for bulk operations across paginated data.
235
+ * The identifier schema is shared by explicit and all-matching selection modes.
236
+ */
225
237
  ```
226
238
 
227
- In `include` mode, the backend targets only `identifiers`. In `exclude` mode, it resolves all entities matching the same filter snapshot and removes `excludedIdentifiers` from that operation.
239
+ The first line summarizes the responsibility. The second line explains behavior, guarantees, or an important
240
+ constraint. Together they should look like a small rectangle instead of one short line followed by a long paragraph.
228
241
 
229
- ### 10. Work with JWT using `ZodJWTService`
242
+ Additional paragraphs should follow the same principle:
230
243
 
231
- ```typescript
232
- import { ZodJWTService } from '@kalutskii/foundation';
233
- import { z } from 'zod';
244
+ - keep related lines visually balanced;
245
+ - separate distinct ideas with an empty JSDoc line;
246
+ - document defaults and feature flags next to the affected option;
247
+ - explain input/output transforms when runtime and static shapes differ;
248
+ - add `@example` only when composition is difficult to understand from the signature;
249
+ - avoid copying a full README usage catalog into source comments.
234
250
 
235
- const payloadSchema = z.object({ userId: z.string() });
236
- const jwt = new ZodJWTService(payloadSchema, { defaultExpirationSeconds: 3600 });
251
+ Private symbols benefit from JSDoc when they encode a non-trivial type relationship or architectural invariant.
252
+ Simple local values do not need ceremonial comments when their names and implementation are already unambiguous.
237
253
 
238
- const token = await jwt.sign({ userId: '42' }, 'secret');
239
- const payload = await jwt.verifyOrThrow(token, 'secret');
240
- // payload: { userId: '42', exp: ... }
254
+ ## Testing
241
255
 
242
- // decode without throwing (returns null on invalid token or schema mismatch)
243
- const decoded = await jwt.decode(token);
244
- ```
256
+ Each module owns exactly one colocated specification file. Do not create separate schema, utility, integration,
257
+ or type fixtures for the same module; runtime and compile-time contracts belong together in `<module>.spec.ts`.
245
258
 
246
- ### 11. Datetime helpers
259
+ A module specification should contain:
247
260
 
248
- ```typescript
249
- import { formatTime, getFormattedDate, getFormattedTime, getZonedTime } from '@kalutskii/foundation';
261
+ - runtime success paths and meaningful boundary failures;
262
+ - strictness, defaults, coercion, transforms, and error behavior where applicable;
263
+ - compile-time equality checks for exported generic contracts;
264
+ - `@ts-expect-error` assertions for intentionally rejected shapes;
265
+ - wide comment sections separating major responsibilities;
266
+ - tests through public APIs instead of unstable dependency internals.
250
267
 
251
- getFormattedTime({ tz: 'Europe/Moscow' }); // '15:30:00 (+3 UTC)'
252
- getFormattedDate({ tz: 'Europe/Moscow' }); // '22.06.2026 15:30:00 (+3 UTC)'
253
- getFormattedDate({ tz: 'Europe/Moscow', withTime: false }); // '22.06.2026'
254
- formatTime(new Date(), { tz: 'Europe/Moscow' }); // '15:30:00, 22 июня 2026 (+3 UTC)'
255
- ```
268
+ Compile-time assertions should use local helper types inside the specification. They must not be exported or moved
269
+ into fixture files. Invalid runtime expressions should remain inside uninvoked functions when module evaluation could
270
+ otherwise throw before Bun starts the suite.
256
271
 
257
- ### 12. Logging
272
+ Tests must remain deterministic:
258
273
 
259
- ```typescript
260
- import { log } from '@kalutskii/foundation';
274
+ - freeze or bound time-dependent behavior;
275
+ - validate random values by shape rather than exact output;
276
+ - avoid relying on terminal-specific ANSI rendering;
277
+ - inspect stable public dependency output instead of private object internals;
278
+ - compare binary responses byte-for-byte through the standard `Response` API.
261
279
 
262
- log.info('Server started', 'app');
263
- log.warn('Deprecated call', 'auth');
264
- log.error('Something failed', 'db', error.stack);
265
- // [15:30:00 (+3 UTC)] app | Server started
266
- ```
280
+ Run the focused specification while implementing, then run the complete suite before finishing the change.
267
281
 
268
- ## Exports
282
+ ## Development workflow
269
283
 
270
- The package exports all public APIs from a single entrypoint:
284
+ ### Prerequisites
271
285
 
272
- - **HTTP**: `success`, `failure`, `fetchSafely`, `fetchAndThrow`, constants, schemas.
273
- - **Hono**: `respond`, `onHandlerError`, `honoLoggingHandler`.
274
- - **Utilities**: `safeExecute`, `measureExecutionTime`, `generateRandomString`, `log`, `getColoredHTTPStatus`, datetime helpers.
275
- - **Zod Bulk**: `zodBulkSelectionSchema`, `ZodBulkSelection`, `ZodBulkIncludeSelection`, `ZodBulkExcludeSelection`.
276
- - **Zod JWT**: `ZodJWTService`.
277
- - **Zod Search**: `zodSearchSchema`, `ZodSearchSchemaOptions`.
278
- - **Zod Pagination**: `zodPaginationSchema`, `zodPaginationShape`, `ZodPaginationOptions`.
279
- - **Zod Flatten**: `asQuerySchema`, `flattenZodShape`, `isZodObject`, `isZodOptional`, `unwrapOptional`, `FlattenZodShape`.
280
- - **Zod Validation**: `asQuery`, `AsQuery`, `AtLeastOne`.
281
- - **Type Utilities**: `Simplify`.
286
+ - Bun compatible with the version used in CI.
287
+ - Node.js only when required by publishing or external tooling.
288
+ - `just` is optional but recommended for the project quality-assurance recipe.
282
289
 
283
- ## Development
290
+ ### Install dependencies
284
291
 
285
292
  ```bash
286
293
  bun install
287
- bun run lint
288
- bun run test
294
+ ```
295
+
296
+ Use the frozen lockfile mode in automation:
297
+
298
+ ```bash
299
+ bun install --frozen-lockfile
300
+ ```
301
+
302
+ ### Focused development
303
+
304
+ ```bash
305
+ bun test src/<module>/<module>.spec.ts
289
306
  bun run typecheck
307
+ bunx eslint src/<module>
308
+ ```
309
+
310
+ Run focused checks first to shorten feedback loops. Do not claim a command passed unless it was actually executed
311
+ and completed successfully in the current working tree.
312
+
313
+ ### Full quality assurance
314
+
315
+ ```bash
316
+ just quality-assurance
290
317
  bun run build
318
+ git diff --check
291
319
  ```
320
+
321
+ The `quality-assurance` recipe runs typecheck, lint, formatting, and the complete test suite. Lint and formatting may
322
+ modify files, so always inspect the resulting diff and ensure unrelated user work has not been changed.
323
+
324
+ The build must succeed because `tsup` generates both runtime ESM and public TypeScript declarations. Review declaration
325
+ output whenever a change introduces conditional generics, schema factories, transforms, or new exported type aliases.
326
+
327
+ ## Adding or changing a module
328
+
329
+ Before considering a module change complete:
330
+
331
+ 1. Confirm the responsibility cannot be owned by a smaller existing abstraction.
332
+ 2. Place files under the correct architectural module and use established suffixes.
333
+ 3. Keep dependency direction consistent with the hierarchy documented above.
334
+ 4. Add or update the single colocated module specification.
335
+ 5. Cover runtime behavior and compile-time inference in that same file.
336
+ 6. Add balanced JSDoc to every newly exported public symbol.
337
+ 7. Export the intended API from `src/index.ts`; keep private helpers private.
338
+ 8. Run focused tests, typecheck, full quality assurance, and declaration build.
339
+ 9. Inspect `git diff`, `git diff --check`, and generated diagnostics.
340
+ 10. Update this README only when architecture or development policy changes.
341
+
342
+ ## Public API and releases
343
+
344
+ The package exposes only the root entrypoint declared in `package.json`. Consumers should import from
345
+ `@kalutskii/foundation` and must not depend on `src` paths or generated bundle internals.
346
+
347
+ A new export is a compatibility commitment. Before publishing it, verify:
348
+
349
+ - the name follows project conventions;
350
+ - the owning module is architecturally correct;
351
+ - runtime validation and static inference agree;
352
+ - public JSDoc explains defaults and constraints;
353
+ - declarations preserve concrete keys, literals, and transformed outputs;
354
+ - the module specification protects the intended contract.
355
+
356
+ Breaking public contracts require an intentional version change and migration plan. Internal refactors should keep
357
+ public names and inferred behavior stable unless the release explicitly communicates otherwise.
package/dist/index.d.ts CHANGED
@@ -72,7 +72,7 @@ declare function respond<T extends object = Record<string, never>, S extends Suc
72
72
  */
73
73
  declare function fileRespond<S extends SuccessStatusCode>(c: Context, options: {
74
74
  status: S;
75
- content: ArrayBuffer;
75
+ content: Uint8Array<ArrayBuffer>;
76
76
  filename: string;
77
77
  contentType?: string;
78
78
  }): Response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalutskii/foundation",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Typescript collection of most common utilities, schemas and functions among private projects.",
5
5
  "type": "module",
6
6
  "repository": {