Skip to content

Commit 04b4aa9

Browse files
committed
Duck-type across defineTool/GrantStore interface versions (CL-5865 follow-up)
Consumers with a newer @intx/agent (static definitions on defineTool options) or @intx/authz (collectGrantsInChain on GrantStore) than this package's own pinned dependency were failing to typecheck the raw source pulled in transitively through the resident distiller's export of the memory tool factories. Both fixes bind the object to a named const before handing it to the typed API instead of a fresh literal, so normal structural (duck-typed) assignment applies rather than TypeScript's excess-property check on literals — the object satisfies either interface shape, and an older caller simply never reads the extra field.
1 parent 6d85cc3 commit 04b4aa9

2 files changed

Lines changed: 37 additions & 11 deletions

File tree

‎src/ports/writable-grant-store.ts‎

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,36 @@ export function createInMemoryWritableGrantStore(
3232
initial: GrantRule[] = [],
3333
): WritableGrantStore & { grants: GrantRule[] } {
3434
const grants = [...initial];
35-
return {
35+
function collect(principalId: string) {
36+
const now = new Date();
37+
return grants.filter((g) => {
38+
if (g.principalId !== principalId) return false;
39+
if (g.expiresAt !== null && g.expiresAt <= now) return false;
40+
return true;
41+
});
42+
}
43+
// Bound to a const rather than returned as a fresh object literal: some
44+
// `@intx/authz` versions require `collectGrantsInChain` on `GrantStore`
45+
// (an ancestor-chain walk) that this package's own pinned version
46+
// predates. Routing through a named variable gets normal structural
47+
// assignment for the function's declared return type instead of
48+
// TypeScript's excess-property check on literals, so this satisfies
49+
// either shape — tenantId (and thus the ancestor chain) is a no-op here
50+
// regardless, matching `createInMemoryGrantStore`'s own tenant-scoped
51+
// fixture semantics.
52+
const store = {
3653
grants,
3754
async collectGrants(principalId: string, _tenantId?: string) {
38-
const now = new Date();
39-
return grants.filter((g) => {
40-
if (g.principalId !== principalId) return false;
41-
if (g.expiresAt !== null && g.expiresAt <= now) return false;
42-
return true;
43-
});
55+
return collect(principalId);
56+
},
57+
async collectGrantsInChain(principalId: string, _tenantId?: string) {
58+
return collect(principalId);
4459
},
4560
async putGrant(grant: GrantRule) {
4661
const idx = grants.findIndex((g) => g.id === grant.id);
4762
if (idx >= 0) grants[idx] = grant;
4863
else grants.push(grant);
4964
},
5065
};
66+
return store;
5167
}

‎src/tools/install.ts‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,19 @@ export function defineMemoryHttpTool(opts: {
3737
signal: AbortSignal | undefined,
3838
) => Promise<string>;
3939
}) {
40-
return defineTool<MemoryInstallEnv>({
40+
// Bound to a const rather than passed as a fresh object literal: some
41+
// `@intx/agent` versions add a static `definitions` field to defineTool's
42+
// options (so callers can enumerate tool names without instantiating the
43+
// factory) that this package's own pinned version predates. Routing
44+
// through a named variable gets normal structural (duck-typed) parameter
45+
// assignment instead of TypeScript's excess-property check on literals,
46+
// so this call type-checks against either shape — the extra field is
47+
// simply unused by an older defineTool at runtime.
48+
const toolOpts = {
4149
id: opts.id,
4250
requires: MEMORY_TOOL_ENV_KEYS,
43-
factory(env) {
51+
definitions: [{ name: opts.name }],
52+
factory(env: MemoryInstallEnv) {
4453
const client = createMemoryHttpClient(readMemoryToolEnv(env));
4554
const runner = createToolRunner([
4655
stringTool({
@@ -55,8 +64,9 @@ export function defineMemoryHttpTool(opts: {
5564
]);
5665
return {
5766
definitions: runner.definitions,
58-
run: (call, signal) => runner.run(call, signal),
67+
run: runner.run.bind(runner),
5968
};
6069
},
61-
});
70+
};
71+
return defineTool<MemoryInstallEnv>(toolOpts);
6272
}

0 commit comments

Comments
 (0)