fenneckit 1.0.4 → 1.2.1

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.
@@ -0,0 +1,72 @@
1
+ # API reference
2
+
3
+ ```ts
4
+ import { newLabs } from "fenneckit";
5
+
6
+ await newLabs(name: string, fn: (kit: LabContext) => void | Promise<void>);
7
+ ```
8
+
9
+ `fn` receives `kit` (the `LabContext`).
10
+
11
+ ## Testing and flow
12
+
13
+ | Method | Description |
14
+ | --- | --- |
15
+ | `kit.test(name, fn)` | Run a named test; returns the test function's result |
16
+ | `kit.done(msg)` | Mark passed |
17
+ | `kit.log(msg)` | Info message |
18
+ | `kit.warning(msg)` | Warning |
19
+ | `kit.flatErr(msg)` | Failure, continue the lab |
20
+ | `kit.err(msg)` | Failure, stop the lab |
21
+ | `kit.out()` | Exit the lab immediately |
22
+ | `kit.ret()` | Restart the lab (max 3 times) |
23
+
24
+ If a test throws an unexpected error it is printed as a bug exception and the lab is marked failed.
25
+
26
+ ## STORE
27
+
28
+ | Method | Description |
29
+ | --- | --- |
30
+ | `setStore(key, value)` | Save |
31
+ | `getStore(key)` | Read |
32
+ | `clearStore(key?)` | Clear one key or all |
33
+ | `setStoreWithTTL(key, value, ms)` | Save with expiry |
34
+
35
+ ## SECRET
36
+
37
+ | Method | Description |
38
+ | --- | --- |
39
+ | `setSecret(key, value)` | Save encrypted |
40
+ | `getSecret(key)` | Read decrypted (`string | undefined`) |
41
+ | `clearSecret(key?)` | Clear one key or all |
42
+ | `setSecretWithTTL(key, value, ms)` | Save with expiry |
43
+
44
+ ## NAMESPACE
45
+
46
+ | Method | Description |
47
+ | --- | --- |
48
+ | `namespace(name)` | Create or get a namespace |
49
+ | `getNamespace(name)` | Get an existing namespace |
50
+ | `ns.set / get / delete / clear / keys / entries / has / toJSON` | Namespace operations |
51
+
52
+ ## TEMP
53
+
54
+ | Method | Description |
55
+ | --- | --- |
56
+ | `setTemp(key, value)` | Save temp value |
57
+ | `getTemp(key)` | Read temp value |
58
+
59
+ ## HTTP
60
+
61
+ See [HTTP Kit](http-kit.md): `get`, `post`, `put`, `patch`, `delete`, `setAuth`, `clearAuth`, `getRequestHistory`, `getLastRequest`, `clearHistory`.
62
+
63
+ ## Audit
64
+
65
+ See [Audit log](audit-log.md): `getLog`, `getLast`, `filterByKey`, `filterByOperation`, `filterByNamespace`, `export`, `clear`.
66
+
67
+ ## CLI
68
+
69
+ ```bash
70
+ npx fenneckit # run all *.labs.js
71
+ npx fenneckit <file> # run one file
72
+ ```
@@ -0,0 +1,47 @@
1
+ # Audit log
2
+
3
+ `kit.audit` records access to STORE and SECRET values so you can trace a workflow.
4
+
5
+ ## What is recorded
6
+
7
+ | Call | Operation |
8
+ | --- | --- |
9
+ | `setStore` | `set` |
10
+ | `getStore` | `get` |
11
+ | `setSecret` | `setSecret` |
12
+ | `getSecret` | `getSecret` |
13
+
14
+ Namespaces, TEMP, and `clear*` calls are not recorded. TTL setters are recorded through their underlying set call.
15
+
16
+ ## Entry fields
17
+
18
+ `timestamp`, `lab`, `test`, `operation`, `key`, `dataType`, `success`, and optionally `namespace`. Secret values are never stored.
19
+
20
+ ## Querying
21
+
22
+ ```ts
23
+ kit.audit.getLog(); // all entries (copy)
24
+ kit.audit.getLast(5); // last 5 entries
25
+ kit.audit.filterByKey("token");
26
+ kit.audit.filterByOperation("setSecret");
27
+ kit.audit.filterByNamespace("user");
28
+ kit.audit.clear();
29
+ ```
30
+
31
+ ## Export
32
+
33
+ ```ts
34
+ const json = kit.audit.export("json"); // pretty-printed JSON
35
+ const csv = kit.audit.export("csv"); // header: timestamp,lab,test,operation,namespace,key,dataType,success
36
+ ```
37
+
38
+ ## Example
39
+
40
+ ```ts
41
+ await kit.test("Review Audit", async () => {
42
+ const last = kit.audit.getLast(5);
43
+ const tokenOps = kit.audit.filterByKey("token");
44
+ kit.log(`${last.length} recent, ${tokenOps.length} on "token"`);
45
+ kit.done("Audit reviewed");
46
+ });
47
+ ```
@@ -0,0 +1,23 @@
1
+ # Best practices
2
+
3
+ 1. **Use STORE for regular data** (IDs, names).
4
+ 2. **Use SECRET for sensitive data** (tokens, passwords, refresh tokens). Never put them in STORE.
5
+ 3. **Use NAMESPACE for organization**: `const user = kit.namespace("user")` instead of `userId`, `userEmail`, ... keys.
6
+ 4. **Check before using**:
7
+ ```ts
8
+ const token = kit.getSecret("token");
9
+ if (!token) kit.err("Token is missing!");
10
+ ```
11
+ 5. **Clean up secrets** in a final test:
12
+ ```ts
13
+ kit.clearSecret("accessToken");
14
+ kit.http.clearAuth();
15
+ ```
16
+ 6. **Check `res.status` yourself.** HTTP error responses are returned, not thrown.
17
+ 7. **Use `flatErr` for non-blocking checks** (e.g. an optional email step) and `err` when later tests cannot continue.
18
+ 8. **Use the audit log** for debugging and traceability, and clear HTTP history before exporting it.
19
+ 9. **One lab, one goal.** Keep labs small and independent, since nothing is shared between labs.
20
+
21
+ ## Good fits
22
+
23
+ API testing, security checks, migration validation, microservice chains, pre-deployment smoke tests, ETL/data pipeline checks, and multi-step integration tests.
@@ -0,0 +1,82 @@
1
+ # Getting started
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ npm install fenneckit@latest --save-dev
7
+ ```
8
+
9
+ Node.js >= 20 is required. FennecKit is an ES module package (`"type": "module"`).
10
+
11
+ ## Your first lab
12
+
13
+ Create `user.labs.js`:
14
+
15
+ ```ts
16
+ import { newLabs } from "fenneckit";
17
+
18
+ await newLabs("User API Test", async (kit) => {
19
+ await kit.test("Step 1: Create User", async () => {
20
+ kit.setStore("userId", "usr_123");
21
+ kit.done("Stored user ID");
22
+ });
23
+
24
+ await kit.test("Step 2: Read User", async () => {
25
+ const userId = kit.getStore("userId");
26
+ kit.log(`Using user: ${userId}`);
27
+ kit.done("Retrieved user");
28
+ });
29
+ });
30
+ ```
31
+
32
+ ## Run
33
+
34
+ ```bash
35
+ npx fenneckit # run every *.labs.js file
36
+ npx fenneckit user.labs.js # run a single file
37
+ ```
38
+
39
+ ## Core ideas
40
+
41
+ | Term | Meaning |
42
+ | --- | --- |
43
+ | Lab | A collection of sequential tests toward one goal |
44
+ | Test | One step, run with `kit.test(name, fn)` |
45
+ | Storage | Data shared by all tests in the same lab |
46
+
47
+ Example lab layout:
48
+
49
+ ```
50
+ 🔬 Lab: Payment Processing
51
+ ├─ Test 1: Validate Card
52
+ ├─ Test 2: Charge Card
53
+ ├─ Test 3: Generate Invoice
54
+ └─ Test 4: Send Receipt Email
55
+ ```
56
+
57
+ ## Reporting results
58
+
59
+ Inside a test, report status with:
60
+
61
+ | Method | Effect |
62
+ | --- | --- |
63
+ | `kit.done(msg)` | Test passed (🟢) |
64
+ | `kit.log(msg)` | Info message (⚪) |
65
+ | `kit.warning(msg)` | Warning (🟡) |
66
+ | `kit.flatErr(msg)` | Failure, lab **continues** (🔴) |
67
+ | `kit.err(msg)` | Failure, lab **stops** (🔴) |
68
+
69
+ ## Flow control
70
+
71
+ - `kit.out()` exits the lab immediately.
72
+ - `kit.ret()` restarts the whole lab, up to 3 times. After that it calls `kit.err("Restart limit exceeded (max 3 attempts)")`.
73
+
74
+ ## Isolation
75
+
76
+ Each `newLabs(...)` call has its own isolated STORE, SECRET, NAMESPACE, TEMP, HTTP client and audit log. Nothing is shared between labs or files.
77
+
78
+ ## Next
79
+
80
+ - [Storage](storage.md)
81
+ - [HTTP Kit](http-kit.md)
82
+ - [API reference](api-reference.md)
@@ -0,0 +1,71 @@
1
+ # HTTP Kit
2
+
3
+ `kit.http` is a built-in client (based on `fetch`) with auth, timeout, retry and request history.
4
+
5
+ ## Methods
6
+
7
+ ```ts
8
+ kit.http.get(url, options?)
9
+ kit.http.post(url, body, options?)
10
+ kit.http.put(url, body, options?)
11
+ kit.http.patch(url, body, options?)
12
+ kit.http.delete(url, options?)
13
+ ```
14
+
15
+ Bodies that are not strings are sent as JSON. Default headers: `Content-Type: application/json` and `User-Agent: FennecKit/1.2.0`.
16
+
17
+ ## Response
18
+
19
+ ```ts
20
+ {
21
+ status: number,
22
+ statusText: string,
23
+ headers: Record<string, string>,
24
+ data: any, // parsed JSON if content-type is application/json, otherwise text
25
+ raw: Response
26
+ }
27
+ ```
28
+
29
+ ## Options
30
+
31
+ | Option | Default | Description |
32
+ | --- | --- | --- |
33
+ | `headers` | none | Extra headers, override defaults |
34
+ | `timeout` | `30000` | Timeout in ms (uses `AbortController`) |
35
+ | `retry.max` | `1` | Total number of **attempts** |
36
+ | `retry.delay` | `1000` | Wait in ms between attempts |
37
+ | `followRedirects` | `true` | Set `false` for manual redirects |
38
+
39
+ ```ts
40
+ const res = await kit.http.get("https://api.example.com/users", {
41
+ timeout: 5000,
42
+ retry: { max: 3, delay: 1000 },
43
+ });
44
+ ```
45
+
46
+ Retries happen only on network errors (thrown by `fetch`). An HTTP 4xx/5xx response is returned normally, so check `res.status` yourself.
47
+
48
+ ## Authentication
49
+
50
+ ```ts
51
+ kit.http.setAuth("bearer", token); // Authorization: Bearer <token>
52
+ kit.http.setAuth("basic", base64Creds); // Authorization: Basic <base64Creds>
53
+ kit.http.setAuth("api-key", key); // X-API-Key: <key>
54
+ kit.http.clearAuth();
55
+ ```
56
+
57
+ For `basic`, pass the credentials already base64-encoded.
58
+
59
+ ## Request history
60
+
61
+ ```ts
62
+ kit.http.getRequestHistory(); // TracedRequest[]
63
+ kit.http.getLastRequest(); // TracedRequest | undefined
64
+ kit.http.clearHistory();
65
+ ```
66
+
67
+ Each entry contains: `id`, `timestamp`, `method`, `url`, `status` (0 on failure), `duration`, `headers`, `requestBody`, `responseBody`, and `error` when the request failed.
68
+
69
+ > History stores request headers (including auth headers) and bodies as-is. Call `kit.http.clearHistory()` before exporting or printing it if it may contain credentials.
70
+
71
+ When all attempts fail, the last error is thrown.
@@ -0,0 +1,90 @@
1
+ # Storage
2
+
3
+ Every lab has five kinds of in-memory storage.
4
+
5
+ | Level | Use for | Encrypted | Audited | Lifetime |
6
+ | --- | --- | --- | --- | --- |
7
+ | STORE | IDs, names, plain values | No | Yes (set/get) | Until lab ends |
8
+ | SECRET | Tokens, passwords | Yes | Yes (set/get) | Until lab ends |
9
+ | NAMESPACE | Grouped entity data | No | No | Until lab ends |
10
+ | TEMP | Scratch values | No | No | Until lab ends |
11
+ | TTL variants | Auto-expiring STORE/SECRET | Same as base | Same as base | Until TTL or lab end |
12
+
13
+ Storage is per lab: nothing is shared across labs or files.
14
+
15
+ ## STORE
16
+
17
+ ```ts
18
+ await kit.test("Create User", async () => {
19
+ const res = await kit.http.post("/users", { name: "Alice" });
20
+ kit.setStore("userId", res.data.id);
21
+ kit.done("User created");
22
+ });
23
+
24
+ await kit.test("Get User", async () => {
25
+ const userId = kit.getStore("userId");
26
+ // ...
27
+ });
28
+ ```
29
+
30
+ - `kit.setStore(key, value)`
31
+ - `kit.getStore(key)`
32
+ - `kit.clearStore(key)` removes one key; `kit.clearStore()` removes everything.
33
+
34
+ ## SECRET
35
+
36
+ Use SECRET for anything sensitive. Values are encrypted in memory with AES-256-GCM (random 16-byte IV and auth tag per value, key derived with scrypt) and decrypted on read.
37
+
38
+ ```ts
39
+ kit.setSecret("accessToken", res.data.accessToken);
40
+ const token = kit.getSecret("accessToken"); // string | undefined
41
+ kit.http.setAuth("bearer", token!);
42
+ kit.clearSecret("accessToken");
43
+ ```
44
+
45
+ - `kit.setSecret(key, value)`, `kit.getSecret(key)`
46
+ - `kit.clearSecret(key)` or `kit.clearSecret()` for all
47
+ - Secret **values** are never written to the lab log or audit log. Only the key name is.
48
+ - If decryption fails, `getSecret` logs an error and returns `undefined`.
49
+
50
+ > Note: the vault protects values held in memory. It uses a built-in default password, so treat it as protection against accidental exposure (logs, dumps), not as a security boundary.
51
+
52
+ ## NAMESPACE
53
+
54
+ Group related values instead of using prefixed keys.
55
+
56
+ ```ts
57
+ const user = kit.namespace("user"); // create or get
58
+ const order = kit.namespace("order");
59
+
60
+ user.set("id", "usr_123");
61
+ order.set("total", 299.99);
62
+
63
+ user.get("id");
64
+ kit.getNamespace("user"); // existing namespace, or undefined
65
+ ```
66
+
67
+ Namespace methods: `set`, `get`, `delete`, `clear(key?)`, `keys`, `entries`, `has`, `toJSON`.
68
+
69
+ ## TEMP
70
+
71
+ Scratch values for short-lived data such as timers.
72
+
73
+ ```ts
74
+ kit.setTemp("startTime", Date.now());
75
+ await kit.http.get("/api/heavy-endpoint");
76
+ const elapsed = Date.now() - kit.getTemp("startTime");
77
+ ```
78
+
79
+ TEMP is cleared when the lab finishes. Do not rely on it as per-test storage; use unique keys or STORE for anything you want to control.
80
+
81
+ ## TTL
82
+
83
+ Values that expire automatically.
84
+
85
+ ```ts
86
+ kit.setStoreWithTTL("sessionId", "sess_123", 30_000); // 30 s
87
+ kit.setSecretWithTTL("tempToken", "token", 60_000); // 60 s
88
+ ```
89
+
90
+ Setting the same key again resets its timer. Expiry is written to the lab log. Pending timers are cleared when the lab ends.
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { newLabs } from "./libs/labs.js";
2
+ import { testing } from "./libs/testing.js";
3
+ export { newLabs, testing };
4
+ export type { TracedRequest, HttpResponse, RequestOptions, AuditEntry } from "./types.ts";
5
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EACL,OAAO,EACP,OAAO,EACR,CAAA;AACD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,UAAU,EACX,MAAM,YAAY,CAAC"}
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { newLabs, getStoreState, clearStore, } from "./libs/labs.js";
1
+ import { newLabs } from "./libs/labs.js";
2
2
  import { testing } from "./libs/testing.js";
3
- export { newLabs, testing, getStoreState, clearStore };
3
+ export { newLabs, testing };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fenneckit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fenneckit.d.ts","sourceRoot":"","sources":["../../src/libs/fenneckit.ts"],"names":[],"mappings":""}
package/libs/labs.d.ts ADDED
@@ -0,0 +1,70 @@
1
+ import type { TracedRequest, HttpResponse, RequestOptions, AuditEntry } from "../types.js";
2
+ declare class NamespacedStore {
3
+ private data;
4
+ private name;
5
+ constructor(name: string);
6
+ set(key: string, value: any): void;
7
+ get(key: string): any;
8
+ delete(key: string): void;
9
+ clear(key?: string): void;
10
+ keys(): string[];
11
+ entries(): [string, any][];
12
+ has(key: string): boolean;
13
+ toJSON(): Record<string, any>;
14
+ }
15
+ declare class AuditLog {
16
+ private entries;
17
+ record(entry: AuditEntry): void;
18
+ getLog(): AuditEntry[];
19
+ getLast(n: number): AuditEntry[];
20
+ filterByKey(key: string): AuditEntry[];
21
+ filterByOperation(op: AuditEntry["operation"]): AuditEntry[];
22
+ filterByNamespace(ns: string): AuditEntry[];
23
+ export(format: "json" | "csv"): string;
24
+ clear(): void;
25
+ }
26
+ interface LabContext {
27
+ out: () => void;
28
+ ret: () => void;
29
+ flatErr: (msg: string) => void;
30
+ err: (msg: string) => void;
31
+ done: (msg: string) => void;
32
+ log: (msg: string) => void;
33
+ warning: (msg: string) => void;
34
+ setStore: (key: string, value: any) => void;
35
+ getStore: (key: string) => any;
36
+ clearStore: (key?: string) => void;
37
+ setSecret: (key: string, value: string) => void;
38
+ getSecret: (key: string) => string | undefined;
39
+ clearSecret: (key?: string) => void;
40
+ namespace: (name: string) => NamespacedStore;
41
+ getNamespace: (name: string) => NamespacedStore | undefined;
42
+ setTemp: (key: string, value: any) => void;
43
+ getTemp: (key: string) => any;
44
+ http: HttpKit;
45
+ audit: AuditLog;
46
+ setStoreWithTTL: (key: string, value: any, ttlMs: number) => void;
47
+ setSecretWithTTL: (key: string, value: string, ttlMs: number) => void;
48
+ test: <T>(testName: string, testFn: () => T | Promise<T>) => Promise<T>;
49
+ }
50
+ declare class HttpKit {
51
+ private authHeader;
52
+ private requestHistory;
53
+ private baseHeaders;
54
+ setAuth(type: "bearer" | "basic" | "api-key", credentials: string): void;
55
+ clearAuth(): void;
56
+ private getAuthHeader;
57
+ private makeRequest;
58
+ private recordRequest;
59
+ get(url: string, options?: RequestOptions): Promise<HttpResponse>;
60
+ post(url: string, body: any, options?: RequestOptions): Promise<HttpResponse>;
61
+ put(url: string, body: any, options?: RequestOptions): Promise<HttpResponse>;
62
+ patch(url: string, body: any, options?: RequestOptions): Promise<HttpResponse>;
63
+ delete(url: string, options?: RequestOptions): Promise<HttpResponse>;
64
+ getRequestHistory(): TracedRequest[];
65
+ clearHistory(): void;
66
+ getLastRequest(): TracedRequest | undefined;
67
+ }
68
+ export declare function newLabs(name: string, fn: (kit: LabContext) => void | Promise<void>, retCount?: number): Promise<void>;
69
+ export {};
70
+ //# sourceMappingURL=labs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labs.d.ts","sourceRoot":"","sources":["../../src/libs/labs.ts"],"names":[],"mappings":"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"}