@flink-app/test-utils 2.0.0-alpha.56 → 2.0.0-alpha.58

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,56 @@
1
1
  # @flink-app/test-utils
2
2
 
3
+ ## 2.0.0-alpha.58
4
+
5
+ ## 2.0.0-alpha.57
6
+
7
+ ### Minor Changes
8
+
9
+ - ef7f495: Add AsyncLocalStorage for automatic user/permissions injection in tools
10
+
11
+ **New Features:**
12
+
13
+ - Tools now receive `user` and `permissions` parameters automatically from AsyncLocalStorage
14
+ - Declarative permissions in tool definitions - framework checks before execution
15
+ - No more need for explicit `agent.withUser()` bindings
16
+ - Context flows automatically: handler → agent → tools
17
+
18
+ **API Changes:**
19
+
20
+ - `ToolExecutor.execute()` signature: `(input, overrides?)` instead of `(input, user?, permissions?)`
21
+ - `FlinkTool` handler signature includes `user?` and `permissions?` parameters
22
+ - Permission checking supports explicit permissions without requiring user object
23
+
24
+ **Testing:**
25
+
26
+ - New test utilities: `withRequestContext()`, `createMockUser()`, `createMockRequestContext()`
27
+ - Override support for unit testing: `execute(input, { user, permissions })`
28
+ - 13 new AsyncLocalStorage integration tests
29
+
30
+ **Developer Experience:**
31
+
32
+ ```typescript
33
+ // Before: Manual user passing
34
+ await agent.withUser(req.user).execute({ message: "..." });
35
+
36
+ // After: Automatic context flow
37
+ await agent.execute({ message: "..." });
38
+
39
+ // Tools with declarative permissions
40
+ export const Tool: FlinkToolProps = {
41
+ permissions: ["car:read"], // ✨ Checked by framework
42
+ };
43
+
44
+ const handler: FlinkTool<Ctx, In, Out> = async ({
45
+ input,
46
+ ctx,
47
+ user,
48
+ permissions, // ✨ Auto-injected
49
+ }) => {
50
+ // Permission already verified - just implement logic
51
+ };
52
+ ```
53
+
3
54
  ## 2.0.0-alpha.56
4
55
 
5
56
  ### Patch Changes
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.56",
3
+ "version": "2.0.0-alpha.58",
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.56"
26
+ "@flink-app/flink": "2.0.0-alpha.58"
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
+ }