@flink-app/test-utils 2.0.0-alpha.55 → 2.0.0-alpha.57

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/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @flink-app/test-utils
2
2
 
3
+ ## 2.0.0-alpha.57
4
+
5
+ ### Minor Changes
6
+
7
+ - ef7f495: Add AsyncLocalStorage for automatic user/permissions injection in tools
8
+
9
+ **New Features:**
10
+
11
+ - Tools now receive `user` and `permissions` parameters automatically from AsyncLocalStorage
12
+ - Declarative permissions in tool definitions - framework checks before execution
13
+ - No more need for explicit `agent.withUser()` bindings
14
+ - Context flows automatically: handler → agent → tools
15
+
16
+ **API Changes:**
17
+
18
+ - `ToolExecutor.execute()` signature: `(input, overrides?)` instead of `(input, user?, permissions?)`
19
+ - `FlinkTool` handler signature includes `user?` and `permissions?` parameters
20
+ - Permission checking supports explicit permissions without requiring user object
21
+
22
+ **Testing:**
23
+
24
+ - New test utilities: `withRequestContext()`, `createMockUser()`, `createMockRequestContext()`
25
+ - Override support for unit testing: `execute(input, { user, permissions })`
26
+ - 13 new AsyncLocalStorage integration tests
27
+
28
+ **Developer Experience:**
29
+
30
+ ```typescript
31
+ // Before: Manual user passing
32
+ await agent.withUser(req.user).execute({ message: "..." });
33
+
34
+ // After: Automatic context flow
35
+ await agent.execute({ message: "..." });
36
+
37
+ // Tools with declarative permissions
38
+ export const Tool: FlinkToolProps = {
39
+ permissions: ["car:read"], // ✨ Checked by framework
40
+ };
41
+
42
+ const handler: FlinkTool<Ctx, In, Out> = async ({
43
+ input,
44
+ ctx,
45
+ user,
46
+ permissions, // ✨ Auto-injected
47
+ }) => {
48
+ // Permission already verified - just implement logic
49
+ };
50
+ ```
51
+
52
+ ## 2.0.0-alpha.56
53
+
54
+ ### Patch Changes
55
+
56
+ - Zod 3.x compat
57
+
3
58
  ## 2.0.0-alpha.55
4
59
 
5
60
  ## 2.0.0-alpha.53
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./http";
2
2
  export * from "./mocks";
3
3
  export * from "./ai";
4
+ export * from "./requestContext";
package/dist/index.js CHANGED
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./http"), exports);
18
18
  __exportStar(require("./mocks"), exports);
19
19
  __exportStar(require("./ai"), exports);
20
+ __exportStar(require("./requestContext"), exports);
@@ -0,0 +1,22 @@
1
+ import { RequestContext } from '@flink-app/flink';
2
+ /**
3
+ * Create a mock request context for testing
4
+ */
5
+ export declare function createMockRequestContext(overrides?: Partial<RequestContext>): RequestContext;
6
+ /**
7
+ * Execute a function within a mocked request context
8
+ * Useful for testing tools and agents that use AsyncLocalStorage
9
+ *
10
+ * @example
11
+ * const result = await withRequestContext(
12
+ * { user: { id: '123', username: 'test' }, reqId: 'req-123' },
13
+ * async () => {
14
+ * return await someTool.execute({ carId: 'car-1' });
15
+ * }
16
+ * );
17
+ */
18
+ export declare function withRequestContext<T>(context: Partial<RequestContext>, fn: () => T | Promise<T>): Promise<T>;
19
+ /**
20
+ * Create a test user with common properties
21
+ */
22
+ export declare function createMockUser(overrides?: any): any;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.createMockRequestContext = createMockRequestContext;
51
+ exports.withRequestContext = withRequestContext;
52
+ exports.createMockUser = createMockUser;
53
+ var flink_1 = require("@flink-app/flink");
54
+ /**
55
+ * Create a mock request context for testing
56
+ */
57
+ function createMockRequestContext(overrides) {
58
+ return __assign({ reqId: "test-req-" + Math.random().toString(36).substring(7), timestamp: Date.now() }, overrides);
59
+ }
60
+ /**
61
+ * Execute a function within a mocked request context
62
+ * Useful for testing tools and agents that use AsyncLocalStorage
63
+ *
64
+ * @example
65
+ * const result = await withRequestContext(
66
+ * { user: { id: '123', username: 'test' }, reqId: 'req-123' },
67
+ * async () => {
68
+ * return await someTool.execute({ carId: 'car-1' });
69
+ * }
70
+ * );
71
+ */
72
+ function withRequestContext(context, fn) {
73
+ return __awaiter(this, void 0, void 0, function () {
74
+ var fullContext;
75
+ return __generator(this, function (_a) {
76
+ fullContext = createMockRequestContext(context);
77
+ return [2 /*return*/, flink_1.requestContext.run(fullContext, fn)];
78
+ });
79
+ });
80
+ }
81
+ /**
82
+ * Create a test user with common properties
83
+ */
84
+ function createMockUser(overrides) {
85
+ return __assign({ id: "user-" + Math.random().toString(36).substring(7), username: "testuser", email: "test@example.com" }, overrides);
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/test-utils",
3
- "version": "2.0.0-alpha.55",
3
+ "version": "2.0.0-alpha.57",
4
4
  "description": "Test utils for Flink",
5
5
  "author": "joel@frost.se",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "jasmine-ts": "^0.3.3",
24
24
  "ts-node": "^10.9.2",
25
25
  "tsc-watch": "^4.2.9",
26
- "@flink-app/flink": "2.0.0-alpha.55"
26
+ "@flink-app/flink": "2.0.0-alpha.57"
27
27
  },
28
28
  "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
29
29
  "scripts": {
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./http";
2
2
  export * from "./mocks";
3
3
  export * from "./ai";
4
+ export * from "./requestContext";
@@ -0,0 +1,44 @@
1
+ import { RequestContext, requestContext } from '@flink-app/flink';
2
+
3
+ /**
4
+ * Create a mock request context for testing
5
+ */
6
+ export function createMockRequestContext(overrides?: Partial<RequestContext>): RequestContext {
7
+ return {
8
+ reqId: "test-req-" + Math.random().toString(36).substring(7),
9
+ timestamp: Date.now(),
10
+ ...overrides,
11
+ };
12
+ }
13
+
14
+ /**
15
+ * Execute a function within a mocked request context
16
+ * Useful for testing tools and agents that use AsyncLocalStorage
17
+ *
18
+ * @example
19
+ * const result = await withRequestContext(
20
+ * { user: { id: '123', username: 'test' }, reqId: 'req-123' },
21
+ * async () => {
22
+ * return await someTool.execute({ carId: 'car-1' });
23
+ * }
24
+ * );
25
+ */
26
+ export async function withRequestContext<T>(
27
+ context: Partial<RequestContext>,
28
+ fn: () => T | Promise<T>
29
+ ): Promise<T> {
30
+ const fullContext = createMockRequestContext(context);
31
+ return requestContext.run(fullContext, fn);
32
+ }
33
+
34
+ /**
35
+ * Create a test user with common properties
36
+ */
37
+ export function createMockUser(overrides?: any) {
38
+ return {
39
+ id: "user-" + Math.random().toString(36).substring(7),
40
+ username: "testuser",
41
+ email: "test@example.com",
42
+ ...overrides,
43
+ };
44
+ }